一、核心问题
直接通过数组下标修改数据时,Vue2无法自动触发视图更新
this.arr[0] = 123; // 修改无效,视图不更新
二、底层原理
Vue2使用Object.defineProperty
实现数据劫持:
// 对对象属性的劫持(有效)
Object.defineProperty(obj, 'key', {
get() { /* 追踪依赖 */ },
set(newVal) { /* 触发更新 */ }
})
三、为什么数组下标无法劫持?
-
技术限制:
Object.defineProperty
只能劫持已存在的属性- 数组下标本质是动态变化的索引,无法预先定义
-
性能灾难:
// 若强行劫持数组下标 const arr = [1,2,3]; for(let i=0; i<arr.length; i++) { Object.defineProperty(arr, i, { ... }) }
- 当数组长度变为10000时,需要循环劫持10000次
- 每次push/pop都要重新劫持所有索引
- 导致内存暴增,性能急剧下降
四、Vue2的妥协方案
-
放弃劫持数组下标,改为重写7个数组方法:
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']
这些方法调用时会触发视图更新:
this.arr.push(4); // 有效更新
-
**$set方法**强制触发更新:
this.$set(this.arr, 0, 123); // 有效更新
五、真实案例对比
// ❌ 无效操作
this.arr.length = 5; // 修改长度无效
this.arr[3] = 'new'; // 通过下标添加元素无效
// ✅ 正确做法
this.$set(this.arr, 3, 'new');
this.arr.splice(3, 1, 'new');
六、Vue3的改进
Vue3改用Proxy
代理整个对象/数组,完美解决此问题:
new Proxy(arr, {
set(target, key, value) {
// 能捕获所有属性的变化,包括动态下标
}
})