导 读
vue3.0中,响应式数据部分弃用了 Object.defineProperty
,使用 Proxy
来代替它。本文将主要通过以下方面来分析为什么vue选择弃用 Object.defineProperty
。
Object.defineProperty
真的无法监测数组下标的变化吗?
分析vue2.x中对数组 Observe
部分源码
对比 Object.defineProperty
和 Proxy
一、无法监控到数组下标的变化?
在一些技术博客上看到过这样一种说法,认为 Object.defineProperty
有一个缺陷是无法监听数组变化:
无法监控到数组下标的变化,导致直接通过数组的下标给数组设置值,不能实时响应。所以vue才设置了7个变异数组( push
、 pop
、 shift
、 unshift
、 splice
、 sort
、 reverse
)的 hack
方法来解决问题。
Object.defineProperty
的第一个缺陷,无法监听数组变化。 然而Vue的文档提到了Vue是可以检测到数组变化的,但是只有以下八种方法, vm.items[indexOfItem] = newValue
这种是无法检测的。
这种说法是有问题的,事实上, Object.defineProperty
本身是可以监控到数组下标的变化的,只是在 Vue 的实现中,从性能/体验的性价比考虑,放弃了这个特性。
下面我们通过一个例子来为 Object.defineProperty
正名:
function defineReactive(data, key, value) { Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function defineGet() { console.log(`get key: ${key} value: ${value}`) return value }, set: function defineSet(newVal) { console.log(`set key: ${key} value: ${newVal}`) value = newVal } }) } function observe(data) { Object.keys(data).forEach(function(key) { defineReactive(data, key, data[key]) }) } let arr = [1, 2, 3] observe(arr)
上面代码对数组arr的每个属性通过 Object.defineProperty
进行劫持,下面我们对数组arr进行操作,看看哪些行为会触发数组的 getter
和 setter
方法。
1. 通过下标获取某个元素和修改某个元素的值
可以看到,通过下标获取某个元素会触发 getter
方法, 设置某个值会触发 setter
方法。
接下来,我们再试一下数组的一些操作方法,看看是否会触发。
2. 数组的 push 方法
push
并未触发 setter
和 getter
方法,数组的下标可以看做是对象中的 key
,这里 push
之后相当于增加了下索引为3的元素,但是并未对新的下标进行 observe
,所以不会触发。
3. 数组的 unshift 方法
我擦,发生了什么?
unshift
操作会导致原来索引为0,1,2,3的值发生变化,这就需要将原来索引为0,1,2,3的值取出来,然后重新赋值,所以取值的过程触发了 getter
,赋值时触发了 setter
。
下面我们尝试通过索引获取一下对应的元素: