Vue 3 中 watch 和 watchEffect 到底该用哪个?一个真实场景的踩坑记录
最近在改一个后台管理系统的筛选面板,需要根据多个筛选条件的变化去重新请求列表数据。最初我用的是 watchEffect 直接在里面调 fetchList,结果遇到一个很隐蔽的问题:fetchList 内部会修改一个 loading ref,而 watchEffect 自动收集依赖时把 loading 也追踪进去了,导致 loading 一变就重新触发请求,形成死循环。后来换成 watch 显式声明依赖才解决。这篇文章就围绕这个真实案例展开,讲清楚 watch 和 watchEffect 在依赖收集机制上的差异,以及在实际业务里怎么选、怎么写才不容易出问题。 ## 先看那个出问题的代码 最初的写法长这样: ```js import { ref, watchEffect } from 'vue' const keyword = ref('') const status = ref('all') const loading = ref(false) const list = ref([]) watchEffect(async () => { loading.value = true const res = await fetch('/api/list', { method: 'POST', body: JSON.stringify({ keyword: keyword.value, status: status.value }) }) list.value = await res.json() loading.value = false }) ``` 跑起来浏览器直接卡死,控制台刷屏请求。原因是 watchEffect 第一次执行时读取了 loading.value(虽然只是赋值,但赋值前需要先访问它吗?其实不是,问题出在 async 之后的第二次执行)。更准确地说,watchEffect 在同步阶段收集依赖:keyword.value、status.value 被读到,同时 loading.value = true 这个赋值操作也会触发 loading 的 setter,而 watchEffect 内部对 ref 的读写是通过 reactive effect 追踪的,赋值本身不会收集依赖,但后续 `loading.value = false` 这个读取(在赋值前其实没有读,但赋值操作在 effect 活跃时会被记录)。实际上 Vue 的 watchEffect 在同步执行期间,任何对响应式属性的访问都会被追踪,包括 setter 内部对 dep 的访问。 具体到这个例子,真正导致循环的是:watchEffect 回调执行时,`loading.value = true` 触发了 loading 的依赖通知,而 watchEffect 自己订阅了 loading(因为在同一 effect 中访问了它),于是又触发一次回调。 ## watchEffect 的依赖收集边界 watchEffect 的规则很简单:同步执行期间访问到的所有响应式属性都会成为依赖。问题在于,异步代码(await 之后)中的访问不会被收集,但同步阶段的赋值操作会。很多人以为“我只是赋值,不算读取”,但 Vue 的响应式系统里,setter 会触发 dep 的 notify,而这个 dep 如果在当前 effect 的依赖集合里,就会重新调度。 更常见的坑是:你在 watchEffect 里调用了某个函数,这个函数内部读了别的 ref,结果这个 ref 的变化也触发了 watchEffect。这本身是 watchEffect 的设计意图,但在请求场景下,loading、error、pagination 这些状态往往和请求参数混在一起,就会互相干扰。 ## 换成 watch 之后的写法 ```js import { ref, watch } from 'vue' const keyword = ref('') const status = ref('all') const loading = ref(false) const list = ref([]) watch( [keyword, status], async ([newKeyword, newStatus]) => { loading.value = true try { const res = await fetch('/api/list', { method: 'POST', body: JSON.stringify({ keyword: newKeyword, status: newStatus }) }) list.value = await res.json() } finally { loading.value = false } }, { immediate: true } ) ``` 依赖被显式锁定为 keyword 和 status,loading 的变化不会再触发请求。immediate: true 对应原来 watchEffect 首次自动执行的行为。如果你需要拿到旧值做对比,watch 还能提供第三个参数 oldValue,这在做“只有 keyword 真正变化时才请求”这类优化时很有用。 ## 什么时候 watchEffect 反而更合适 不是所有场景都要用 watch。比如你需要监听一个动态计算出来的表达式,而这个表达式依赖的 ref 数量不固定,或者你确实希望“任何相关状态变化都重新执行”,那 watchEffect 更省事。典型场景是本地缓存同步: ```js watchEffect(() => { localStorage.setItem('draft', JSON.stringify({ title: title.value, content: content.value, tags: tags.value })) }) ``` 这里没有异步,没有请求,所有依赖都是明确要监听的,watchEffect 写起来比 watch 数组更简洁。但即便如此,也要注意不要在 watchEffect 里修改它自己依赖的响应式数据。 ## 几个实际项目里的判断标准 看到“请求”两个字,优先用 watch。请求通常意味着你要控制触发时机、要处理竞态、要避免重复请求,显式依赖比自动收集更安全。看到“纯同步的副作用”,比如写 localStorage、更新 DOM 之外的第三方库实例、打日志,watchEffect 可以接受。 还有一个容易忽略的点:watch 的 flush 选项。默认是 pre,在组件更新前执行。如果你需要在 DOM 更新后拿到最新的布局信息再发请求,要改成 post。watchEffect 同样支持 flush,但很多人根本不知道,写完发现请求时机不对就开始怀疑人生。 ```js watch(keyword, fetchList, { flush: 'post' }) ``` 另外,watch 可以随时通过返回的 stop 函数停止监听,在组合式函数里封装请求逻辑时,配合 onUnmounted 手动停止,能避免组件卸载后请求回来还在改状态导致的警告。watchEffect 也有 stop,但自动收集依赖的写法更容易让人忘记清理。 最后说一个不太常见但值得知道的细节:watch 的源可以是一个 getter 函数。当你需要监听一个对象里的深层属性但又不想开 deep 时,getter 比直接传对象更精准。 ```js watch( () => form.user.id, (newId) => fetchUserDetail(newId) ) ``` 这种写法只会在 user.id 变化时触发,user 下其他字段改动不会引起请求。在表单场景里比 deep: true 性能好得多,也更符合直觉。 推荐查看天气:https://www.0dtq.com/qianan/7tian.html 推荐查看天气:https://www.0dtq.com/qianan/10tian.html 推荐查看天气:https://www.0dtq.com/qianan/15tian.html 推荐查看天气:https://www.0dtq.com/qianan/40tian.html 推荐查看天气:https://www.0dtq.com/songyuanfuyu/ 推荐查看天气:https://www.0dtq.com/songyuanfuyu/zuotian.html 推荐查看天气:https://www.0dtq.com/songyuanfuyu/7tian.html 推荐查看天气:https://www.0dtq.com/songyuanfuyu/10tian.html 推荐查看天气:https://www.0dtq.com/songyuanfuyu/15tian.html 推荐查看天气:https://www.0dtq.com/songyuanfuyu/40tian.html 推荐查看天气:https://www.0dtq.com/yanbian/ 推荐查看天气:https://www.0dtq.com/yanbian/zuotian.html 推荐查看天气:https://www.0dtq.com/yanbian/7tian.html 推荐查看天气:https://www.0dtq.com/yanbian/10tian.html 推荐查看天气:https://www.0dtq.com/yanbian/15tian.html 推荐查看天气:https://www.0dtq.com/yanbian/40tian.html 推荐查看天气:https://www.0dtq.com/yanji/ 推荐查看天气:https://www.0dtq.com/yanji/zuotian.html 推荐查看天气:https://www.0dtq.com/yanji/7tian.html 推荐查看天气:https://www.0dtq.com/yanji/10tian.html 推荐查看天气:https://www.0dtq.com/yanji/15tian.html 推荐查看天气:https://www.0dtq.com/yanji/40tian.html 推荐查看天气:https://www.0dtq.com/tumen/ 推荐查看天气:https://www.0dtq.com/tumen/zuotian.html 推荐查看天气:https://www.0dtq.com/tumen/7tian.html 推荐查看天气:https://www.0dtq.com/tumen/10tian.html 推荐查看天气:https://www.0dtq.com/tumen/15tian.html 推荐查看天气:https://www.0dtq.com/tumen/40tian.html 推荐查看天气:https://www.0dtq.com/dunhua/ 推荐查看天气:https://www.0dtq.com/dunhua/zuotian.html 推荐查看天气:https://www.0dtq.com/dunhua/7tian.html 推荐查看天气:https://www.0dtq.com/dunhua/10tian.html 推荐查看天气:https://www.0dtq.com/dunhua/15tian.html 推荐查看天气:https://www.0dtq.com/dunhua/40tian.html 推荐查看天气:https://www.0dtq.com/huichun/ 推荐查看天气:https://www.0dtq.com/huichun/zuotian.html 推荐查看天气:https://www.0dtq.com/huichun/7tian.html 推荐查看天气:https://www.0dtq.com/huichun/10tian.html 推荐查看天气:https://www.0dtq.com/huichun/15tian.html 推荐查看天气:https://www.0dtq.com/huichun/40tian.html 推荐查看天气:https://www.0dtq.com/longjing/ 推荐查看天气:https://www.0dtq.com/longjing/zuotian.html 推荐查看天气:https://www.0dtq.com/longjing/7tian.html 推荐查看天气:https://www.0dtq.com/longjing/10tian.html 推荐查看天气:https://www.0dtq.com/longjing/15tian.html 推荐查看天气:https://www.0dtq.com/longjing/40tian.html 推荐查看天气:https://www.0dtq.com/helong/ 推荐查看天气:https://www.0dtq.com/helong/zuotian.html 推荐查看天气:https://www.0dtq.com/helong/7tian.html 推荐查看天气:https://www.0dtq.com/helong/10tian.html 推荐查看天气:https://www.0dtq.com/helong/15tian.html 推荐查看天气:https://www.0dtq.com/helong/40tian.html 推荐查看天气:https://www.0dtq.com/wangqing/ 推荐查看天气:https://www.0dtq.com/wangqing/zuotian.html 推荐查看天气:https://www.0dtq.com/wangqing/7tian.html 推荐查看天气:https://www.0dtq.com/wangqing/10tian.html 推荐查看天气:https://www.0dtq.com/wangqing/15tian.html 推荐查看天气:https://www.0dtq.com/wangqing/40tian.html 推荐查看天气:https://www.0dtq.com/antu/ 推荐查看天气:https://www.0dtq.com/antu/zuotian.html 推荐查看天气:https://www.0dtq.com/antu/7tian.html 推荐查看天气:https://www.0dtq.com/antu/10tian.html 推荐查看天气:https://www.0dtq.com/antu/15tian.html 推荐查看天气:https://www.0dtq.com/antu/40tian.html 推荐查看天气:https://www.0dtq.com/haerbin/ 推荐查看天气:https://www.0dtq.com/haerbin/zuotian.html 推荐查看天气:https://www.0dtq.com/haerbin/7tian.html 推荐查看天气:https://www.0dtq.com/haerbin/10tian.html 推荐查看天气:https://www.0dtq.com/haerbin/15tian.html 推荐查看天气:https://www.0dtq.com/haerbin/40tian.html 推荐查看天气:https://www.0dtq.com/daoli/ 推荐查看天气:https://www.0dtq.com/daoli/zuotian.html 推荐查看天气:https://www.0dtq.com/daoli/7tian.html 推荐查看天气:https://www.0dtq.com/daoli/10tian.html 推荐查看天气:https://www.0dtq.com/daoli/15tian.html 推荐查看天气:https://www.0dtq.com/daoli/40tian.html 推荐查看天气:https://www.0dtq.com/nangang/ 推荐查看天气:https://www.0dtq.com/nangang/zuotian.html 推荐查看天气:https://www.0dtq.com/nangang/7tian.html 推荐查看天气:https://www.0dtq.com/nangang/10tian.html 推荐查看天气:https://www.0dtq.com/nangang/15tian.html 推荐查看天气:https://www.0dtq.com/nangang/40tian.html 推荐查看天气:https://www.0dtq.com/daowai/ 推荐查看天气:https://www.0dtq.com/daowai/zuotian.html 推荐查看天气:https://www.0dtq.com/daowai/7tian.html 推荐查看天气:https://www.0dtq.com/daowai/10tian.html 推荐查看天气:https://www.0dtq.com/daowai/15tian.html 推荐查看天气:https://www.0dtq.com/daowai/40tian.html 推荐查看天气:https://www.0dtq.com/pingfang/ 推荐查看天气:https://www.0dtq.com/pingfang/zuotian.html 推荐查看天气:https://www.0dtq.com/pingfang/7tian.html 推荐查看天气:https://www.0dtq.com/pingfang/10tian.html 推荐查看天气:https://www.0dtq.com/pingfang/15tian.html 推荐查看天气:https://www.0dtq.com/pingfang/40tian.html 推荐查看天气:https://www.0dtq.com/songbei/ 推荐查看天气:https://www.0dtq.com/songbei/zuotian.html 推荐查看天气:https://www.0dtq.com/songbei/7tian.html 推荐查看天气:https://www.0dtq.com/songbei/10tian.html 推荐查看天气:https://www.0dtq.com/songbei/15tian.html 推荐查看天气:https://www.0dtq.com/songbei/40tian.html 推荐查看天气:https://www.0dtq.com/xiangfang/ 推荐查看天气:https://www.0dtq.com/xiangfang/zuotian.html 推荐查看天气:https://www.0dtq.com/xiangfang/7tian.html 推荐查看天气:https://www.0dtq.com/xiangfang/10tian.html 推荐查看天气:https://www.0dtq.com/xiangfang/15tian.html 推荐查看天气:https://www.0dtq.com/xiangfang/40tian.html 推荐查看天气:https://www.0dtq.com/hulan/ 推荐查看天气:https://www.0dtq.com/hulan/zuotian.html 推荐查看天气:https://www.0dtq.com/hulan/7tian.html 推荐查看天气:https://www.0dtq.com/hulan/10tian.html 推荐查看天气:https://www.0dtq.com/hulan/15tian.html 推荐查看天气:https://www.0dtq.com/hulan/40tian.html 推荐查看天气:https://www.0dtq.com/acheng/ 推荐查看天气:https://www.0dtq.com/acheng/zuotian.html 推荐查看天气:https://www.0dtq.com/acheng/7tian.html 推荐查看天气:https://www.0dtq.com/acheng/10tian.html 推荐查看天气:https://www.0dtq.com/acheng/15tian.html 推荐查看天气:https://www.0dtq.com/acheng/40tian.html 推荐查看天气:https://www.0dtq.com/haerbinyilan/ 推荐查看天气:https://www.0dtq.com/haerbinyilan/zuotian.html 推荐查看天气:https://www.0dtq.com/haerbinyilan/7tian.html 推荐查看天气:https://www.0dtq.com/haerbinyilan/10tian.html 推荐查看天气:https://www.0dtq.com/haerbinyilan/15tian.html 推荐查看天气:https://www.0dtq.com/haerbinyilan/40tian.html 推荐查看天气:https://www.0dtq.com/fangzheng/ 推荐查看天气:https://www.0dtq.com/fangzheng/zuotian.html 推荐查看天气:https://www.0dtq.com/fangzheng/7tian.html 推荐查看天气:https://www.0dtq.com/fangzheng/10tian.html 推荐查看天气:https://www.0dtq.com/fangzheng/15tian.html 推荐查看天气:https://www.0dtq.com/fangzheng/40tian.html 推荐查看天气:https://www.0dtq.com/haerbinbinxian/ 推荐查看天气:https://www.0dtq.com/haerbinbinxian/zuotian.html 推荐查看天气:https://www.0dtq.com/haerbinbinxian/7tian.html 推荐查看天气:https://www.0dtq.com/haerbinbinxian/10tian.html 推荐查看天气:https://www.0dtq.com/haerbinbinxian/15tian.html 推荐查看天气:https://www.0dtq.com/haerbinbinxian/40tian.html 推荐查看天气:https://www.0dtq.com/bayan/ 推荐查看天气:https://www.0dtq.com/bayan/zuotian.html 推荐查看天气:https://www.0dtq.com/bayan/7tian.html 推荐查看天气:https://www.0dtq.com/bayan/10tian.html 推荐查看天气:https://www.0dtq.com/bayan/15tian.html 推荐查看天气:https://www.0dtq.com/bayan/40tian.html 推荐查看天气:https://www.0dtq.com/mulan/ 推荐查看天气:https://www.0dtq.com/mulan/zuotian.html 推荐查看天气:https://www.0dtq.com/mulan/7tian.html 推荐查看天气:https://www.0dtq.com/mulan/10tian.html 推荐查看天气:https://www.0dtq.com/mulan/15tian.html 推荐查看天气:https://www.0dtq.com/mulan/40tian.html 推荐查看天气:https://www.0dtq.com/tonghe/ 推荐查看天气:https://www.0dtq.com/tonghe/zuotian.html 推荐查看天气:https://www.0dtq.com/tonghe/7tian.html 推荐查看天气:https://www.0dtq.com/tonghe/10tian.html 推荐查看天气:https://www.0dtq.com/tonghe/15tian.html 推荐查看天气:https://www.0dtq.com/tonghe/40tian.html 推荐查看天气:https://www.0dtq.com/yanshou/ 推荐查看天气:https://www.0dtq.com/yanshou/zuotian.html 推荐查看天气:https://www.0dtq.com/yanshou/7tian.html 推荐查看天气:https://www.0dtq.com/yanshou/10tian.html 推荐查看天气:https://www.0dtq.com/yanshou/15tian.html 推荐查看天气:https://www.0dtq.com/yanshou/40tian.html 推荐查看天气:https://www.0dtq.com/shangzhi/ 推荐查看天气:https://www.0dtq.com/shangzhi/zuotian.html 推荐查看天气:https://www.0dtq.com/shangzhi/7tian.html 推荐查看天气:https://www.0dtq.com/shangzhi/10tian.html 推荐查看天气:https://www.0dtq.com/shangzhi/15tian.html 推荐查看天气:https://www.0dtq.com/shangzhi/40tian.html 推荐查看天气:https://www.0dtq.com/haerbinwuchang/ 推荐查看天气:https://www.0dtq.com/haerbinwuchang/zuotian.html 推荐查看天气:https://www.0dtq.com/haerbinwuchang/7tian.html 推荐查看天气:https://www.0dtq.com/haerbinwuchang/10tian.html 推荐查看天气:https://www.0dtq.com/haerbinwuchang/15tian.html 推荐查看天气:https://www.0dtq.com/haerbinwuchang/40tian.html 推荐查看天气:https://www.0dtq.com/qiqihaer/ 推荐查看天气:https://www.0dtq.com/qiqihaer/zuotian.html 推荐查看天气:https://www.0dtq.com/qiqihaer/7tian.html 推荐查看天气:https://www.0dtq.com/qiqihaer/10tian.html 推荐查看天气:https://www.0dtq.com/qiqihaer/15tian.html 推荐查看天气:https://www.0dtq.com/qiqihaer/40tian.html 推荐查看天气:https://www.0dtq.com/longsha/ 推荐查看天气:https://www.0dtq.com/longsha/zuotian.html 推荐查看天气:https://www.0dtq.com/longsha/7tian.html 推荐查看天气:https://www.0dtq.com/longsha/10tian.html 推荐查看天气:https://www.0dtq.com/longsha/15tian.html 推荐查看天气:https://www.0dtq.com/longsha/40tian.html 推荐查看天气:https://www.0dtq.com/jianhua/ 推荐查看天气:https://www.0dtq.com/jianhua/zuotian.html 推荐查看天气:https://www.0dtq.com/jianhua/7tian.html 推荐查看天气:https://www.0dtq.com/jianhua/10tian.html 推荐查看天气:https://www.0dtq.com/jianhua/15tian.html 推荐查看天气:https://www.0dtq.com/jianhua/40tian.html 推荐查看天气:https://www.0dtq.com/tiefeng/ 推荐查看天气:https://www.0dtq.com/tiefeng/zuotian.html 推荐查看天气:https://www.0dtq.com/tiefeng/7tian.html 推荐查看天气:https://www.0dtq.com/tiefeng/10tian.html 推荐查看天气:https://www.0dtq.com/tiefeng/15tian.html 推荐查看天气:https://www.0dtq.com/tiefeng/40tian.html 推荐查看天气:https://www.0dtq.com/angangxi/ 推荐查看天气:https://www.0dtq.com/angangxi/zuotian.html 推荐查看天气:https://www.0dtq.com/angangxi/7tian.html 推荐查看天气:https://www.0dtq.com/angangxi/10tian.html