开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 3 天,点击查看活动详情
说点题外话
这一篇我将从vue2和vue3双向绑定的底层原理的Proxy
和Object.defineProp
方法入手,详细的说说为vue3为什么解决了vue2的缺点
说正文
vue2响应式的缺点?
1. Object.defineProperty 针对对象的缺点
我们都知道vue2的底层原理是基于Object.defineProperty
的。整个方法的缺点也很明显,我们就先看看Object.defineProperty整个方法的缺点,从代码入手
const foo = (obj, key, value) => {
Object.defineProperty(obj, key, {
get() {
console.log(`key:${key}`);
return value
},
set(newValue) {
console.log(`${key}的值由${value}变成${newValue}`);
value = newValue === value ? value : newValue
}
})
}
const data = { name: "小白" }
Object.keys(data).map(key => foo(data, key, data[key]))
console.log(data.name);
此时打印结果为 key:name | 小白
,说明只是触发了foo函数的get方法,如果我们将 data对象里面name属性的值呢?
data.name = "小刚"
console.log(data.name);
此时的输出结果是 name的值由小白变成小刚 | key:name | 小刚
, 因为我们修改name属性的值,触发了foo函数的set
方法,name属性的值也跟着更新了。
那么当我们在data对象上添加一个新的属性呢?
data.age = 18
console.log(data.age);
此时的打印结果为 18
, 说明既没有触发get方法也没有触发set方法。由此我们也可以得出一条vue2的缺点:Object.defineProperty
只对初始化对象的属性有作用,对于新增的鹅属性没有监听作用。官方也提出了使用$set
方法来解决这个问题。
2. Object.defineProperty针对数组的缺点
我们总说在vue2中Object.defineProperty不能侦听数组下标的变化,那么这个方法是真的不能侦听数组下标吗?实际上Object.defineProperty这个方法是可以侦听数组下标变化的,但是vue2摒弃了这个特性,所以我们直接设置数组下标做不到响应式。
const foo = (obj, key, value) => {
Object.defineProperty(obj, key, {
get() {
console.log(`get=== key: ${key}=== value: ${value}`);
return value
},
set(newValue) {
console.log(`set=== key: ${key}=== value: ${newValue}`);
value = newValue
}
})
}
const obsever = (data) => {
Object.keys(data).map(key => foo(data, key, data[key]))
}
let arr = [1, 2, 3, 4]
obsever(arr)
arr[1]
当我们通过数组下标获取值结果: get=== key: 1=== value: 2
, 会触发get方法
arr[1] = 8
通过数组下标改变值结果是: set=== key: 1=== value: 8
, 会触发set方法
push
方法
arr.push(5)
我们会发现什么都没有输出,说明既没有触发get方法也没有触发set方法。Object.defineProperty
是有监控数组下标变化的能力的,push是在原来数组的基础上新增,Object.defineProperty是监控原有数组下标的变化,所以push不会触发get方法和set方法。
unshift
方法
arr.unshift(0)
因为unit是在数组的前面添加值,所以需要将原来索引值为0,1,2,3的值取出来重新赋值执行unift之后的值。所以输出结果是上图。
那还有一个疑问,为什么原来value值是4的没有执行set方法呢,这就要再说一遍Object.defineProperty
只能监控原有数组下标的变化。
这里我们对比对象来看,数组arr原数组是[1, 2, 3, 4], 所以只有索引为0,1,2,3的值的发生变化的时候才会触发我们写的observe方法。对于新增的索引就相当于对象中新增的属性一样,不会侦听新增的变化。
** pop
方法**
arr.pop()
arr[3] = 8
输出的结果为get=== key: 3=== value: 4
, 因为执行pop方法将索引为3的值删除掉了,所以我们再去修改这个索引值是不会触发observe方法的。
3. 总结
Object.defineProperty是有监控数组下标的能力的,但是vue2放弃了这个特性
- 通过索引获取或者修改对应的值时,可以触发get和set方法
- 通过push和unshift新增新的索引,需要手动初始化才能执行get方法和set方法
- 通过pop或者shift删除索引,会删除索引并更新索引,也会触发set方法和get方法。
Object.defineProperty
在数组中的表现和对象中的表现是一样的,你可以将对象的key看作是数组的索引。
说再见
这一篇说了vue2底层原理Object.defineProperty的缺点,下一篇我们继续说vue3的优势以及是如何觉得vue2的痛点的。
难忘今宵
肚子饿为什么会咕咕叫?
当忽然闻到食物的香味、看到诱人的食物是,人的嘴里会自动分泌唾液,胃部也开始分泌胃酸,会快速消化掉胃里的食物,人们就会因为饥饿而肚子咕咕叫。