【Vue2】解决数组监听问题
在 Vue2 中,响应式系统是通过 Object.defineProperty 实现的,它可以劫持对象的属性 getter/setter
,但有两个大问题:
- 不能监听数组的索引变动(
arr[0] = xxx
无法触发更新)。 - 不能监听数组的 length 变化(
arr.length = 0
无法触发更新)。
🔑 Vue2 的解决办法
Vue2 在初始化时,会 重写数组的 7 个变更方法,让它们在调用时不仅修改数据,还能通知视图更新。
这 7 个方法是:
push
pop
shift
unshift
splice
sort
reverse
实现思路:
-
拿到数组原型上的方法(
Array.prototype.push
等)。 -
新建一个对象,继承
Array.prototype
。 -
在这个对象上重写上面 7 个方法:
- 调用原始方法完成数据修改;
- 手动触发
dep.notify()
,告诉依赖进行视图更新。
源码简化版(伪代码)
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {arrayMethods[method] = function (...args) {// 先调用原始方法const result = arrayProto[method].apply(this, args);// 通知依赖更新this.__ob__.dep.notify();return result;}});
当 Vue2 检测到一个属性是数组时,就会把它的原型指向 arrayMethods
,从而拦截这几个操作。
🔒 局限性
- 直接修改数组索引:
vm.arr[1] = 100
不会触发更新。 - 修改
length
:vm.arr.length = 0
不会触发更新。
解决办法:
- 使用
Vue.set(vm.arr, index, value)
或this.$set(vm.arr, index, value)
。 - 使用
splice
替代:vm.arr.splice(index, 1, value)
。
🆕 Vue3 的变化
Vue3 用 Proxy 重写了响应式系统,能直接监听数组的索引和 length 变化,不需要像 Vue2 那样 hack。
👉 总结:
Vue2 通过重写数组的 7 个变更方法来解决数组监听问题,但索引和 length 的变动仍然无法直接监听,只能用
Vue.set
或splice
替代。Vue3 使用 Proxy 完美解决了这个限制。