众所周知,Vue2.0 的双向数据绑定原理是基于 数据劫持 和 发布订阅,数据劫持部分用的就是Object.defineProperty();但是Vue 3.0 已经做出优化,改为 Proxy() 代理的方式。
1.优缺点比较
- Object.defineProperty():将一个js对象传递给vue实例对象的data()时,vue通过Object.defineProperty来遍历目标对象内部的属性。
- 优点: 更为准确
- 缺点:无法对数据下标进行监控,需要遍历对象,效率低
- Proxy代理:将传入对象,及对象内部全部改为Proxy代理的方式。
- 优点:可以监听整个对象,不必遍历,当然数组也可以。
- 缺点:兼容性
2.实现的对比
这里借用我上课时老师写的例子 ^_^
先来看看Object.defineProperty怎么玩的。。。
<input type="text" id="textInput">
输入:<span id="textSpan"></span>
var obj = {},
textInput = document.querySelector("#textInput"), //输入
textSpan = document.querySelector("#textSpan"); //输出
Object.defineProperty(obj, 'name', {
get: function () {
console.log('get');
return v
},
set: function (val) {
console.log('set');
v = val
}
});
textInput.onkeyup = function () {
obj.name = textInput.value;
textSpan.innerHTML = obj.name;
}
此时,在输入框键入字符,可以看到get set均会被调用。
如果是数组呢???
var Obj = {}
Object.defineProperty(Obj, 'a', {
get: function () {
console.log('get');
return v
},
set: function (val) {
console.log('set');
v = val
}
});
Obj.a = [] // set 设置a为[], 调用set 这里没问题
Obj.a.push('1') // get 这里也是设置a里面的元素啊,为什么会调用get???
Obj.a[0] = 1 // get ??? why
Obj.a.pop(1) // get ??? why
Obj.a = [1, 2, 3] // 为什么这里又是 set 了呢?
来解释一下上面的结果:
-
Obj.a = []; // 这里对Obj的属性a做出赋值操作,set
-
Obj.a.push('1'); Obj.a.pop(1); // 这些是对属性a的内部进行操作,Object.defineProperty就不行了。
-
Obj.a = [1, 2, 3];// 这里对属性a的整体进行了赋值操作,set
总结: 尽管对于Object.defineProperty做了优化处理,但是在监控数组下标时还是有问题。
再来看看Proxy是怎么玩的。。。
var arr = [];
var p = new Proxy(arr, {
get: (target, key) => {
console.log('get')
return key in target ? target[key] : undefined
},
set: (target, key, value) => {
console.log('set')
target[key] = value
return true
}
})
p.push(1);
get // 获取数组arr的push方法
get // 获取数组arr的length属性
set // 设置arr[0] = 1
set // 设置数组arr长度为1
可以看到,p.push(1); // Proxy对于数组的元素,数组的长度 分别进行了监听。