watcheffect通过自动追踪函数内读取的响应式值(如ref.value、reactive属性)来建立依赖,变化时自动重执行;支持oninvalidate清理异步任务,返回stop函数可手动停止监听,适用于搜索、表单联动等动态依赖场景。

watchEffect 能让异步数据同步逻辑变简洁,关键在于它不靠你“告诉它监听谁”,而是自己在运行时看“你读了哪些响应式值”——读到的自动成为依赖,一变就重跑。它天生适合处理搜索、表单联动、自动保存这类依赖动态、副作用明确的场景。
自动追踪怎么发生的?
你写一个函数传给 watchEffect,Vue 就会立刻执行它一次。执行过程中,只要你的代码访问了 ref.value、reactive 对象的属性、或 computed 的值,Vue 就通过 Proxy 的 get 拦截悄悄记下来。这些被读取的响应式数据,就是它的依赖列表。下次其中任意一个变了,整个函数再跑一遍,重新收集依赖 —— 不用你手动列数组,也不怕漏掉深层字段。
- 比如函数里用了 searchTerm.value 和 user.profile.city,这两个就是依赖;改任何一个,函数都重执行
- 如果函数里有 if 分支,只执行到的那条路径里的响应式读取才会被追踪,没进的分支不会建立依赖
- 它不支持 deep 监听对象内部变化,想监听 {a: {b: 1}} 里 b 的变化,得直接读 user.a.b,而不是只读 user.a
异步请求怎么避免竞态问题?
用户快速输入搜索词时,前一个请求还没回来,后一个已经发出去了,旧结果可能覆盖新状态。watchEffect 提供 onInvalidate(也叫 onCleanup)来安全清理上一次未完成的操作。
- 在函数参数里接收 onInvalidate,然后在发起异步任务前,把它注册为清理函数
- 例如用 AbortController:创建 controller → fetch 时传 signal → 立即调 onInvalidate(() => controller.abort())
- 定时器、事件监听器、WebSocket 连接等,也都用同样方式清理
- 注意:清理函数必须同步执行,不能是 async;它只在下一次 watchEffect 执行前调用,首次不会触发
什么时候该停掉它?
watchEffect 返回一个 stop 函数,调用后就彻底停止监听和后续执行。这不是可选项,而是良好实践。
- 最常见是在组件卸载时清理:onUnmounted(stop)
- 如果某个条件满足后不再需要监听(比如表单提交成功),可主动调用 stop()
- 不手动 stop,组件销毁后 watchEffect 仍可能尝试执行(尤其含异步逻辑时),造成内存泄漏或报错
和 watch 比,什么情况优先选它?
当你不需要对比“变之前是什么”,只关心“现在变成啥了就要做点事”,watchEffect 更轻量直接。
- 多个分散的 ref 需要一起响应(比如分页参数 page、size、sortField),不用拼数组
- 依赖本身会变(如根据权限决定监听 apiToken 还是 mockData),watch 很难写清楚源
- 逻辑集中在一个函数里,比如“读取所有过滤条件 → 拼 URL → 发请求 → 更新 loading 和 data”
- watch 更适合需要旧值、要监听非响应式变量、或需 deep/flush 等精细控制的场景










