Vue 3 的 watch 默认不触发对象内部属性变更,需用函数式监听(如 () => person.name)或启用 deep 选项;函数式监听可获准确 oldValue,deep 模式下 oldValue 不可靠。

Vue 3 的 watch 默认不触发对象内部属性的变更,比如 person.name = '李四' 不会触发监听 person 整体的回调——除非你明确启用深度监视或改用函数式监听。
用函数包裹属性路径(最推荐)
监听对象某个具体字段时,必须把访问逻辑写成 getter 函数。这样 Vue 才能追踪到该路径下的响应式依赖:
-
正确写法(触发精准、oldValue 可用):
const person = reactive({ name: '张三', job: { title: '工程师' } })watch(() => person.name, (n, o) => console.log('name 变了', o, '→', n)) -
嵌套属性也一样:
watch(() => person.job.title, (n, o) => console.log('title 更新了')) - ⚠️ 注意:不能直接传
person.job,否则监听的是整个job对象引用,内部属性变不会触发;必须用箭头函数访问到底层值。
需要监听整个对象的所有变化?开 deep
当你确实要响应对象任意层级的变更(如表单数据整体校验),且不想逐个写函数时,可配合 { deep: true }:
审查 Vue 管理后台的变更请求(Vue 2 + Element UI,Vue 3 + Element Plus)。重点关注最小化变更实现、上线风险检查以及回归检查清单生成。
watch(person, (n, o) => { /* … */ }, { deep: true })- ✅ 有效:修改
person.job.salary = 25000会触发 - ❌ 限制:
oldValue在deep模式下不可靠(常与newValue相同),这是 Vue 3 当前行为,不是 bug - ? 小技巧:如果既要深度响应又要保留旧值,建议拆成多个函数式监听,或用
watchEffect+ 手动缓存上一次快照
ref 包裹的对象,watch 方式一致
即使是 ref({ ... }),监听深层属性仍需函数式写法:
const user = ref({ profile: { avatar: '' } })-
watch(() => user.value.profile.avatar, (n, o) => {...})✅ -
watch(user, ..., { deep: true })❌ 不推荐——ref 的 value 是一层包装,deep 容易误判;函数式更可控
别踩坑:reactive 直接传入 watch 的陷阱
写成 watch(person, callback) 看似简洁,但有隐性行为:
- 它自动等效于
{ deep: true },无法关闭 -
oldValue始终失效(返回和newValue相同的 Proxy) - 性能略低(每次变更都遍历整棵树)
- ✅ 正确替代:只监听你真正关心的字段,用函数式 + 必要时加
deep
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










