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的响应式系统要复杂得多,它还涉及依赖收集和派发更新等机制。