本人正在前端历劫中,写文章的主要目的是为了让自己理解知识更透彻,忘了随时可以翻阅一下。如果同时也能帮到其他正在历劫的小伙伴当然更高兴l。
前面一篇文章写了关于VUE2.0中实现数据双向绑定的核心知识点--Object.defineProperty,今天继续探讨下VUE2.X为何弃用Object.defineProperty。
一门技术的被弃用,肯定是因为技术本身存在诸多限制或不足或有了更完美的替代品。
首先探讨下VUE2.0中Object.defineProperty存在的问题\
为了让读者有更直观的一个印象,我这里先将VUE2.0中Object.defineProperty存在的问题罗列出来
- 不能监听数组索引和长度的变更
- 无法监听 属性的添加和删除
- 必须遍历对象的每个属性, 必须深层遍历嵌套的对象。
Object.defineProperty
是对对象属性的操作,所以需要对对象进行深度遍历去对属性进行操作。
注:很多小伙伴可能有疑问,不是说VUE2.0中Object.defineProperty不能监听数组索引和长度的变更吗,为什么我们在使用vue2的时候,一样可以监听数组索引和长度的变更? 我们得明白主体,我们只是说Object.defineProperty不能,并没说vue2.0不能。并不能说因为vue2.0使用了Object.defineProperty,vue2.0就不能监听数组内部属性的变化了。而vue2之所以能监听,是vue2.0对数组相关的方法或其他进行了重写。当然vue2.0中还是存在无法监听直接修改数组中某一项值和数组长度,如ar[0]=1, arr.length=12是无法监听的,针对这个vue2.0有其他解决方案,请看下文。
问题1、不能监听数组索引和长度的变更
例:
const obj = {};
let initValue = 1;
Object.defineProperty(obj, 'name', {
set: function(value) {
console.log('set方法被执行了');
initValue = value;
},
get: function() {
return initValue;
}
});
console.log(obj.name); // 1
obj.name = []; // 会执行set方法,会打印信息
// 给 obj 中的name属性 设置为 数组 [1, 2, 3], 会执行set方法,会打印信息
obj.name = [1, 2, 3];
// 然后对 obj.name 中的某一项进行改变值,不会执行set方法,不会打印信息
obj.name[0] = 11;
// 然后我们打印下 obj.name 的值
console.log(obj.name);
// 然后我们使用数组中push方法对 obj.name数组添加属性 不会执行set方法,不会打印信息
obj.name.push(4);
//使用数组中unshift方法对 obj.name数组添加属性 不会执行set方法,不会打印信息
obj.name.unshift(5)
console.log(obj.name,"删除前");//删除前
obj.name.pop() //删除obj.name数组最后一个元素
console.log(obj.name,"删除后");//删除后,最后一个元素被删除了,但是set方法并没有执行,
obj.name.length = 5; // 也不会执行set方法
console.log(obj.name.length,obj.name[3])
下图是运行结果\
从运行结果可以看到,当我们改变数组某一项的值,给数组添加或删除属性、或改变数组长度
,都没有执行set方法
。也就是如果我们对数组中的内部属性值直接更改的话,都不会触发set方法。
因此如果我们想实现数据双向绑定的话,我们就不能简单地使用 obj.name[1] = newValue这样的来进行赋值了。那么对于vue这样的框架,那么一般会重写 Array.property.push方法,并且生成一个新的数组赋值给数据,这样数据双向绑定就触发了。
问题2、 无法监听 属性的添加和删除\
在vue2中使用中你可能遇到过这样的问题
# template
<div @click="add(obj.a)">{{ obj.a }}</div>
<div @click="addb(obj.b)">{{ obj.b }}</div>
# srcript
data () {
return {
obj:{
a:1
}
}
},
mounted () {
this.obj.b = 1; //给对象新增属性b
},
methods: {
addb(item){
item += 1;
console.log(this.obj)
}
}
当点击obj.a是响应式, 页面也会更新
而点击新增的obj.b则不会更新。
所以Object.defineProperty无法监听到新增的对象属性,删除也一样\
针对这个问题vue2的解决方案:
新增对象属性$set
Vue.set(object, 'key', value) //第一个参数是添加属性的对象,第二个参数是要添加的属性,第二个参数是要添加的属性的值
this.$set(object, 'key', value)
//上面两种写法都一样
this.obj = Object.assign({},this.obj, { b: 1, e: 2 })
this.obj = {...this.obj,...{ b: 3, e: 2 }}
数组解决方案类似 关于vue无法侦听数组及对象属性的变化的解决方案
删除对象属性 $delete
Vue.delete(target,'object')//第二个参数是字符串[也就是我们要删除的属性]
this.$delete(target,'object')
问题3、 必须遍历对象的每个属性, 必须深层遍历嵌套的对象。
Object.defineProperty
是对对象属性的操作,所以需要对对象进行深度遍历去对属性进行操作。
使用 Object.defineProperty() 多数要配合 Object.keys() 和遍历,,于是多了一层嵌套.
Object.keys(obj).forEach(key => {
Object.defineProperty(obj, key, {
// ...
})
})
当一个对象为深层嵌套的时候,必须进行逐层遍历,直到把每个对象的每个属性都调用 Object.defineProperty() 为止。
简单通俗的理解Vue3.0中的Proxy
vue2.x实现数据的响应部分源码
Proxy使用详解
深入理解 Object.defineProperty 及实现数据双向绑定
vue2数据响应原理核心 Object.defineProperty() 深入浅出的讲解
# Object.defineProperty与Proxy理解整理