Vue3 的 watch 默认只监听引用变化,需用 deep: true 配合 getter 函数监听深层属性;推荐直接监听具体路径(如 user.profile.name)以提升性能和准确性;监听多个字段可用数组形式。

Vue3 的 watch 默认只监听响应式对象的**引用变化**,不会自动追踪内部嵌套属性的修改。要让深层属性(比如 user.profile.address.city)变化时也触发回调,必须显式启用深度监听或采用更精准的监听方式。
用 deep: true 监听整个对象
这是最常用的方式,适用于需要响应对象任意层级变动的场景。注意:必须配合 getter 函数使用,不能直接传入响应式对象本身(否则会强制深度监听且无法获取正确的 oldValue)。
- ✅ 正确写法:用箭头函数包裹对象,再加
{ deep: true } - ❌ 错误写法:
watch(user, callback, { deep: true })—— 这种写法在 Vue3 中会忽略deep配置,且oldValue不可靠
示例:
javascriptconst user = reactive({
profile: {
name: '张三',
address: { city: '北京' }
}
})
watch(
() => user, // 必须是 getter 函数
(newVal, oldVal) => {
console.log('user 或其任意子属性变了')
},
{ deep: true }
)
只监听某一层级的特定属性(推荐)
如果只需响应某个具体字段(如 user.profile.name),直接监听该路径即可,无需开启 deep,性能更好、逻辑更清晰。
立即学习“前端免费学习笔记(深入)”;
- 适合明确知道关注哪个字段的场景
- 能准确拿到
oldValue和newValue - 不依赖深度遍历,响应更快
示例:
javascriptwatch(
() => user.profile.name,
(newName, oldName) => {
console.log(`姓名从 ${oldName} 变为 ${newName}`)
}
)
监听多个嵌套字段或组合条件
当需要同时响应几个关键嵌套属性(比如 user.profile.name 和 user.status)时,可以把它们包装成数组传入 watch。
- 回调函数接收两个数组参数:
[new1, new2]和[old1, old2] - 依然保持精确监听,避免无谓的深度扫描
示例:
javascriptwatch(
() => [user.profile.name, user.status],
([newName, newStatus], [oldName, oldStatus]) => {
if (newName !== oldName) console.log('名字变了')
if (newStatus !== oldStatus) console.log('状态变了')
}
)
常见不触发原因和解决办法
深层属性更新后 watch 没反应?多数情况是以下问题:
-
直接修改非响应式对象:确保原始对象是用
reactive或ref创建的,普通对象赋值不会触发响应 -
新增属性未被代理:对
reactive对象添加全新属性(如user.newField = 'xxx')可能不响应,建议用Vue.set或改用ref包裹整个对象 -
异步更新时机问题:确认修改发生在组件已挂载、watch 已注册之后;必要时加
nextTick确保 DOM 和响应式系统同步 -
监听了错误的源:比如监听
user却期望user.profile.name改变时触发,但没加deep: true,就会静默失败










