Vue 2 中使用Object.defineProperty
来实现其响应式系统存在一些限制和问题:
-
深度检测: Vue 2中对于对象的处理是递归的;对于每个属性,Vue会逐层使用
Object.defineProperty
将其转换成 getter/setter。这样,当你访问或修改嵌套较深的属性时(如a.b.c
),Vue已经提前将a
、a.b
和a.b.c
的属性转换为响应式,能够追踪它们的变化。 -
数组限制:
Object.defineProperty
无法检测到数组索引的变化,因此Vue重写了数组的变异方法(如push
、pop
、splice
等)来实现对数组的响应式监听。 -
对象属性添加或删除的限制: 因为
Object.defineProperty
只能在初始化的时候应用于属性,当你在一个已经创建的Vue实例上添加新属性时,这个新属性是非响应式的。如果你想要它是响应式的,需要使用Vue.set()
或this.$set()
方法添加新属性。 -
性能问题: 因为
Object.defineProperty
是递归地对对象的每一个属性进行处理,所以在处理具有大量属性或深层嵌套对象时,可能会有较大的性能开销。
关于处理a.b.c
类型的属性,Vue 2内部会递归地遍历对象a
的所有属性,为它们各自使用Object.defineProperty
定义getter和setter。如果b
是a
的属性,那么同样会针对b
做这样的处理,以及它的所有属性,包括c
等。这样,在访问或修改a.b.c
时,Vue可以追踪到这些变化并触发相关的更新。
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: function reactiveGetter() {
// 依赖收集等操作
return val;
},
set: function reactiveSetter(newVal) {
if (newVal === val) return;
val = newVal;
// 触发更新视图等操作
},
});
// 如果val本身还是对象,则递归处理
if (typeof val === "object") {
reactive(val);
}
}
function reactive(obj) {
for (let key in obj) {
defineReactive(obj, key, obj[key]);
}
}
在上面的reactive
函数中,我们将一个对象转换成响应式对象。这是Vue内部实现响应式的简化版原理。不过,Vue的响应式系统要复杂得多,它还涉及依赖收集和派发更新等机制。