Vue watch默认不响应对象内部属性变化,需设deep: true实现深度监听;推荐精准监听特定路径或用computed提取后再watch,兼顾性能与准确性。

Vue 的 watch 默认只监听对象的引用变化,内部属性改了也不会触发——这不是 bug,是设计使然。要让深层属性修改也能触发监听,核心就一条:启用深度监听(deep: true),但得用对方式。
直接监听整个对象 + deep: true
这是最常用也最稳妥的方式,适合需要响应对象任意嵌套层级变更的场景。
- 组合式 API 中,把 reactive 对象本身传入 watch,并显式加
{ deep: true } - 选项式 API 中,在 watch 配置里为该字段设置
handler和deep: true - 注意:
deep: true会递归遍历所有嵌套属性,性能开销随对象复杂度上升,不建议对大型对象无脑开启
精准监听某个深层路径(推荐)
如果只关心 user.profile.avatar 这类固定路径,没必要监听整个对象。用 getter 函数返回目标属性,既轻量又准确。
递归分析 Vue 项目组件依赖,从入口文件生成组件层级图,支持 Vue 2/3,输出组件名、文件路径和属性。适用于分析组件结构、排查依赖或了解项目架构。
- 组合式 API:
watch(() => state.user.profile.avatar, (newVal) => {...}) - 选项式 API:
watch: { 'user.profile.avatar': (newVal) => {...} } - 这种方式不依赖
deep,也不触发多余监听,修改该路径时一定响应
避免常见坑:数组、引用、初始化
有些情况看似“改了但没触发”,其实和 deep 无关,而是机制理解偏差。
立即学习“前端免费学习笔记(深入)”;
- 监听数组时,
push/pop等变异方法会触发,无需deep: true;但直接通过索引赋值(如arr[0] = xxx)或修改 length 不会响应,需用Vue.set或替换整个数组 -
deep: true下,oldVal和newVal可能是同一对象引用,内容变了但地址没变,对比要用深比较(如JSON.stringify或专用库) - 想一上来就执行一次回调?加
immediate: true,否则只在数据首次变化后才触发
替代方案:计算属性 + 普通 watch
当路径较深或逻辑较复杂时,可先用 computed 提取关注值,再对计算属性做简单监听。
- 比如
computed: { avatarUrl() { return this.user?.profile?.avatar || '' } } - 然后
watch: { avatarUrl(newVal) { ... } } - 好处是逻辑解耦、可复用、调试友好,且天然支持响应式链路

















