Vue 的 watch 默认监听引用变化而非内容变化,数组用 push/pop/splice 等方法可触发(Vue 2 重写7个方法,Vue 3 需 deep: true 或 watchEffect),索引赋值或 length 修改不触发;推荐按场景选择监听方式,避免盲目 deep,注意响应性丢失与性能问题。

Vue 的 watch 默认监听的是引用变化,不是内容变化。数组被 push、pop、splice 等方法修改时,只要没替换整个数组,watch 就可能不触发——这不是 bug,是设计使然。
为什么数组变化常“监听不到”
Vue 2 和 Vue 3 对数组的响应式处理逻辑不同,但核心限制一致:
- Vue 2 重写了 7 个变更方法(push、pop、shift、unshift、splice、sort、reverse),这些操作能触发 watch;但直接通过索引赋值(
this.arr[0] = 'x')或修改length不会触发 - Vue 3 使用 Proxy,默认只做浅监听:只有
arr = [...arr, newItem]这类整体替换才触发回调;arr.push()或arr[0].name = 'y'不触发(除非启用deep: true) - watch 回调中拿到的
newValue和oldValue在深度监听下可能是同一引用,无法靠 === 判断是否真变了
三种可靠监听方式选对场景
不用盲目加 deep: true,先看你要监听什么:
从零开始编排 SpringBoot+Vue 全栈项目工作流,适用于新建全栈项目或完整脚手架请求,触发后依次调用 product‑spec‑builder(需求调研)等模块。
-
监听数组结构变动(增删改项、长度变化):Vue 2 直接 watch 数组名即可;Vue 3 推荐用
watch(() => arr, callback)+{ deep: true },或改用watchEffect自动追踪依赖 -
监听某一项的某个字段(如
list[0].status):直接写路径watch(() => list[0].status, ...),精准高效,无需 deep -
监听数组长度:Vue 2/3 都支持
watch(() => list.length, ...),轻量且稳定
避开常见陷阱的操作建议
很多“监听失效”其实源于写法不当:
- 避免直接索引赋值:不用
arr[i] = val,改用arr.splice(i, 1, val)(Vue 2/3 都兼容)或Vue.set(arr, i, val)(仅 Vue 2) - Vue 3 中 reactive 数组尽量别用 ref 包裹再解构,否则会丢失响应性;保持原始 reactive 引用
- 需要立即执行回调?加上
{ immediate: true };需要手动停掉监听?把 watch 返回值存起来,后续调用它即可 - 深层对象数组(如
list: [{user: {name: ''}}])要监听user.name,必须确保user本身也是 reactive,否则 deep 也无效
性能与可维护性提醒
深度监听开销明显,尤其数组大、嵌套深时:
- 优先监听具体路径(如
list[0].id),而非整个list - 组件卸载前记得调用
stopWatch(),防止内存泄漏 - 若只是为更新视图,多数情况用
v-for+ 计算属性更自然;watch 更适合发请求、打日志、同步外部状态等副作用操作
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










