watchEffect 不触发的根本原因是依赖追踪仅发生在首次同步执行期,异步代码、ref嵌套对象未整块替换、深层属性未显式读取均导致依赖未建立;应依场景选用watch、deep选项、reactive或computed替代。

watchEffect 不触发,不是代码写错了,而是它根本没“看见”你改的那个值——它的依赖追踪有明确边界,越界就失效。
同步执行期才是追踪窗口
watchEffect 只在函数首次同步运行时收集依赖。一旦遇到 await、setTimeout 或 Promise.then 这类异步分界点,后续访问的响应式数据就不再纳入追踪范围。
- ✅ 会触发:
console.log(count.value)在 await 前 → count 被 track - ❌ 不触发:await 后的
console.log(msg.value)→ msg 没被 track,改了也无反应 - ⚠️ 注意:即使 msg 是 ref,只要它在异步块里才被读取,watchEffect 就不会为它建立响应联系
ref 包裹对象时,只认“整块替换”
当你用 const obj = ref({ name: 'jinD' }),watchEffect 默认追踪的是 obj 这个 ref 实例本身,而不是它内部的 { name: 'jinD' }。
- ✅ 会触发:
obj.value = { name: 'ding123' }→ 替换了整个 value,ref 的 setter 触发 effect - ❌ 不触发:
obj.value.name = 'ding1'→ 只改了 proxy 内部属性,track 时 target 是 ref 实例,trigger 时 target 却是内部对象,不匹配 - ? 解法:加
{ deep: true }选项(仅 watch 支持),或改用watch(() => obj.value.name, ...)精准监听路径
深层属性没被读取,等于没存在
watchEffect 不会预扫描或递归探测对象结构。它只对实际执行到的 getter 调用做 track —— 如果某条路径压根没被访问,就不会建立依赖。
- ❌
console.log(obj)不会触发任何深层依赖收集(只打印 Proxy,未触发其内部 getter) - ✅
console.log(obj.value.name)才真正读取并 track 了 name 属性 - ? 验证技巧:在 getter 里加
console.log('get!'),就能看到哪些属性实际参与了响应链
替代方案比硬扛更高效
遇到上述盲区,强行绕开反而增加维护成本。更自然的做法是换工具或调整结构:
- 需要监听深层固定路径?→ 用
watch(() => state.user.profile.avatar, ...) - 要响应对象任意改动且结构可控?→ 把目标对象用
reactive声明,再传给watch(..., { deep: true }) - 副作用逻辑本身就含异步?→ 拆成两个 watchEffect:一个同步收依赖,一个由它触发异步任务
- 想保持自动追踪又需深度能力?→ 组合
computed提取关注值,再watch计算属性(解耦 + 稳定响应)

















