watchEffect 仅追踪响应式数据变化,对普通变量、解构props、非响应式函数返回值等无效;需保持访问路径为响应式链路(如props.name、ref.value.xxx),避免提前解包或封装隐藏依赖。

watchEffect 不会自动追踪非响应式变量的变化,这是它失效的最常见原因。
watchEffect 只响应响应式数据
watchEffect 的原理是依赖收集,仅对 ref、reactive、computed 等响应式对象中的属性读取行为进行追踪。如果在回调中访问的是普通 JS 变量(如 let count = 0)、props 的解构值、或从函数返回的非响应式对象,这些变化不会触发重新执行。
- ❌ 错误示例:对解构后的 props 属性使用 watchEffect const { id } = toRefs(props);
- ✅ 正确做法:始终通过 toRefs 或直接访问 props.xxx watchEffect(() => console.log(props.name)); // props 是响应式对象,可追踪
watchEffect(() => console.log(id.value)); // id 是 ref,OK
const { name } = props; // 普通字符串,非响应式
watchEffect(() => console.log(name)); // name 改变不会触发
避免在 watchEffect 中使用非响应式中间变量
一旦把响应式数据“提取”成普通变量,就脱离了响应式系统。
- ❌ 错误示例:提前解包、赋值给 const/let const userRef = ref({ name: 'Alice' });
- ✅ 正确做法:保持访问路径为响应式链路 watchEffect(() => console.log(userRef.value.name)); // ✅ 触发更新
const userName = userRef.value.name; // 普通字符串
watchEffect(() => console.log(userName)); // 即使 userRef.value.name 后续变了,也不触发
// 或用 reactive + .name 访问
注意 watchEffect 内部调用的函数是否“隐藏”了响应式访问
如果封装逻辑到外部函数,且该函数内部没有直接读取响应式数据,watchEffect 就无法收集依赖。
- ❌ 错误示例:工具函数屏蔽了响应式访问 function getName(u) { return u.name; }
- ✅ 正确做法:确保响应式属性在 watchEffect 回调内被直接读取 watchEffect(() => console.log(user.name)); // ✅ 明确读取
const user = reactive({ name: 'Bob' });
watchEffect(() => console.log(getName(user))); // ❌ 依赖收集不到 user.name
调试技巧:检查是否真的触发了依赖收集
可在 watchEffect 回调开头加一句 console.log('run'),配合 Vue Devtools 的 “Reactivity” 面板观察依赖项,或使用 effectScope({ lazy: true }) 手动控制并打印 track 信息。
- 临时加日志确认执行时机
- 在 Devtools 中查看当前 active effect 的 dependencies 列表
- 改用 watch 并显式指定 source,便于定位来源(如 watch(() => props.id, ...)
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











