Vue 2 中 watch 默认监听不到数组索引赋值(如 arr[0] = newValue)和修改 length 的变化,因 Object.defineProperty 无法拦截;响应式操作有 push、pop、shift、unshift、splice、sort、reverse;非响应式操作包括索引赋值、length 修改及直接替换引用(除非 deep: true)。

Vue 2 中 watch 默认监听不到数组**通过索引直接赋值**(如 arr[0] = newValue)或**修改数组长度**(如 arr.length = 0)的变化,根本原因在于 Vue 2 的响应式系统基于 Object.defineProperty,而该 API 无法拦截这两种操作。
哪些数组操作是响应式的?
Vue 2 重写了数组的 7 个变更方法,调用它们会触发视图更新和 watch 回调:
-
push()、pop() -
shift()、unshift() -
splice()、sort()、reverse()
✅ 正确示例:this.list.push(item) 或 this.list.splice(1, 1, newItem) —— watch 能捕获。
哪些数组操作是非响应式的?
以下写法不会触发响应式更新,watch 也监听不到:
-
this.list[0] = 'new'(索引直接赋值) -
this.list.length = 0(清空长度) -
this.list = [...this.list, item](替换整个引用,但 watch 需配置deep: true才能捕获内容变化)
⚠️ 注意:即使替换了数组引用,如果 watch 没开启深度监听,也可能错过内部元素变化。
watch 数组的正确写法
要可靠监听数组内容变化,推荐组合使用以下方式:
-
监听数组引用 +
deep: true:watch: { list: { handler() { /* … */ }, deep: true } }
这样能捕获所有深层变化(包括 push/splice 后的内容变动),但性能略低,适合小数组。 -
用计算属性做中间层:
将依赖数组逻辑提取到computed,再watch计算属性。例如:computed: { listCount() { return this.list.length } },然后监听listCount。 -
主动触发更新(兼容非响应式操作):
若必须用this.list[0] = x,随后调用this.$set(this.list, 0, x)或this.$forceUpdate()(不推荐后者)。
Vue 3 的改进
Vue 3 使用 Proxy 代理整个数组对象,天然支持索引赋值、length 修改等所有操作,watch 默认即可监听。但注意:watch 默认是浅监听,仍需 deep: true 才能响应嵌套对象内部变化;
若只关心数组长度或是否为空,监听 list.length 或用计算属性更高效。

















