注:本文提到的Vue2版本是2.6.10;
双向绑定:监听对象属性变化的底层方法不同;
2中使用的是Object.defineProperty;3中则使用Proxy拦截;
Object.defineProperty
简单的写个方法,让监听对象属性的setter和getter
javascript
function isObj(obj) {
return typeof obj === 'object' && obj !== null;
}
function observe(obj) {
if (isObj(obj)) {
Object.keys(obj).forEach(property => {
const v = obj[property]
if (isObj(v)) {
observe(v);
} else {
Object.defineProperty(obj, property, {
get() {
console.log('get');
return v;
},
set(newValue) {
if (newValue !== v) {
console.log('set');
v = newValue;
}
},
enumerable: true,
configurable: true,
});
}
})
}
}
const a = {
a: 1,
b: 'b',
c: {
ca: 1,
cb: 'b',
}
}
observe(a);

结果如上:可以监听到对应属性的setter和getter。
但是有个弊端,监听不到对象属性的删除和增加,对于数组方法也没办法监听。
- 解决办法
- 监听不到对象属性的删除和增加
typescript
// 使用this.$set实现
this.task.status = 'pending'; // 视图无变化
this.$set(this.task, 'status', 'pending'); // 视图变化
this.$delete(this.task, 'status'); // 视图变化

- 对于数组方法也没办法监听:
直接插入了一个新的原型对象,拦截数组原型上的方法
Proxy
typescript
const proxy = new Proxy(a, {
get: function (obj, prop) {
console.log('get');
return obj[prop];
},
set: function (obj, prop, value) {
if (obj[prop] !== value) {
console.log('set');
obj[prop] = value
}
},
})
Proxy直接可以监听对象属性变化,不需要对属性进行深层遍历,还有一些其他的方法扩展,详见Proxy
总结:Object.defineProperty只能遍历对象属性进行劫持,Proxy直接劫持整个对象,3的性能好些,也好理解属性变化的监听,但是不支持低版本浏览器。但是Proxy拦截方法多(apply、ownKeys、deleteProperty等),2只能通过其他方法辅助实现,对应打包体积也会变大。