在 Vue 2 中,为了实现对数组的响应式监听,Vue 对数组的某些原生方法进行了重写。这是因为 JavaScript 的限制,Vue 无法直接通过 Object.defineProperty
监听数组的变化。以下是 Vue 2 修改的数组方法及其原因:
1. Vue 2 修改的数组方法
Vue 2 重写了以下数组方法,使其能够触发视图更新:
push()
:向数组末尾添加一个或多个元素。pop()
:移除数组的最后一个元素。shift()
:移除数组的第一个元素。unshift()
:向数组开头添加一个或多个元素。splice()
:从数组中添加/删除元素。sort()
:对数组进行排序。reverse()
:反转数组的顺序。
2. 为什么需要重写这些方法?
(1)JavaScript 的限制
- Vue 2 使用
Object.defineProperty
实现响应式数据。Object.defineProperty
只能监听对象属性的 读取 和 赋值,无法直接监听数组的变化(如push
、pop
等操作)。 - 数组的
length
属性也无法通过Object.defineProperty
监听。
(2)重写方法的实现
Vue 2 通过重写数组的原型方法,在调用这些方法时,除了执行原生操作外,还会触发视图更新。
示例:
const arrayProto = Array.prototype; // 获取数组的原型
const arrayMethods = Object.create(arrayProto); // 创建一个新的对象,继承数组的原型
// 重写数组方法
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach((method) => {
const original = arrayProto[method]; // 缓存原生方法
Object.defineProperty(arrayMethods, method, {
value: function mutator(...args) {
const result = original.apply(this, args); // 调用原生方法
const ob = this.__ob__; // 获取 Observer 实例
ob.dep.notify(); // 触发依赖更新
return result;
},
enumerable: false,
writable: true,
configurable: true,
});
});
(3)如何替换数组的原型
Vue 2 在将数组转换为响应式数据时,会将数组的原型替换为上述重写后的原型。
function observeArray(arr) {
arr.__proto__ = arrayMethods; // 替换数组的原型
}
3. Vue 2 中数组的局限性
尽管 Vue 2 重写了数组方法,但仍有一些操作无法触发视图更新:
- 直接通过索引修改数组元素:
this.items[0] = 'new value'; // 不会触发视图更新
- 修改数组长度:
this.items.length = 0; // 不会触发视图更新
解决方法:
- 使用
Vue.set
或this.$set
修改数组元素:
this.$set(this.items, 0, 'new value'); // 触发视图更新
- 使用
splice
修改数组长度:
this.items.splice(0); // 触发视图更新
4. Vue 3 的改进
在 Vue 3 中,使用 Proxy
替代了 Object.defineProperty
,能够直接监听数组的变化,包括通过索引修改元素和修改数组长度。因此,Vue 3 不再需要重写数组方法。
总结
- Vue 2 重写的数组方法:
push
、pop
、shift
、unshift
、splice
、sort
、reverse
。 - 重写的原因:
Object.defineProperty
无法直接监听数组的变化,重写方法可以触发视图更新。 - 局限性:直接通过索引修改数组元素或修改数组长度不会触发更新,需要使用
Vue.set
或splice
。 - Vue 3 的改进:使用
Proxy
直接监听数组变化,无需重写数组方法。
理解 Vue 2 对数组方法的处理,可以帮助你更好地使用 Vue 的响应式系统,并避免常见的陷阱。
THE END
暂无评论内容