当数据模型data变化时,页面视图会得到响应更新
vue其实现原理是对data的getter/setter方法进行拦截(Object.defineProperty或者Proxy),利用发布订阅的设计模式,在getter方法中进行订阅,在setter方法中发布通知,让所有订阅者完成响应。
说这些的时候我们在刚使用vue2.x的就会遇到过数据更新了啊,为何页面不更新呢。这其实就是Object.defineProperty在作祟。
而在vue3还没有发布时,很火的一个话题就是Vue3将使用Proxy 取代Vue2 版本的Object.defineProperty。那么Proxy
对比Object.defineProperty
有什么优势。
Proxy
对比Object.defineProperty
Object.defineProperty只能劫持对象的属性,需要遍历对象的每一个属性,如果属性值也是对象,就需要递归进行深度遍历。
Object.defineProperty劫持的是对象的属性,所以新增属性时,需要重新遍历对象, 对其新增属性再次使用Object.defineProperty进行劫持。也就是Vue2.x中给数组和对象新增属性时,需要使用$set才能保证新增的属性也是响应式的, $set内部也是通过调用Object.defineProperty去处理的。
Object.defineProperty无法监听数组数据的变化,但是为什么数组在使用push pop等方法的时候可以触发页面更新呢,那是因为vue内部拦截了这些方法。比如数组在使用push pop等方法的时候为什么可以触发页面更新呢,那是因为vue内部拦截了这些方法。
// 重写push等方法,然后再把原型指回原方法
var ARRAY_METHOD = [ 'push', 'pop', 'shift', 'unshift', 'reverse', 'sort', 'splice' ];
var array_methods = Object.create(Array.prototype);
ARRAY_METHOD.forEach(method => {
array_methods[method] = function () {
// 拦截方法
return Array.prototype[method].apply(this, arguments);
}
});
回归正题我们分别用代码简单的实现下Proxy
对比Object.defineProperty
如何实现绑定的
// 这是将要被劫持的对象
const data = {
name: '',
};
// 遍历对象,对其属性值进行劫持
Object.keys(data).forEach(function(key) {
console.log('1')
Object.defineProperty(data, key, {
enumerable: true,
configurable: true,
get: function(newVal) {
return val
},
set: function(newVal) {
// 当属性值发生变化时我们可以进行额外操作
console.log(`大家好,我是${newVal}我被劫持了`);
val = newVal;
},
});
});
data.name = '111';
const target = {
name: '控制'
};
const handler = {
get: function(target, key) {
console.log(`${key} 被读取`);
return target[key];
},
set: function(target, key, value) {
console.log(`${key} 被设置为 ${value}`);
target[key] = value;
}
};
const testObj = new Proxy(target, handler);
console.log(testObj.name); // name 被读取 及输出名字 控制
testObj.name = 1; // name 被设置为 1 输出 1
该方法的含义是:用于拦截某个属性的读取操作。它有三个参数,如下解析:
该方法是用来拦截某个属性的赋值操作,它可以接受四个参数,参数解析分别如下:
该方法是判断某个目标对象是否有该属性名。接收二个参数,分别为目标对象和属性名。返回的是一个布尔型。
该方法是用来拦截new命令的,它接收三个参数,分别为 目标对象,构造函数的参数对象及创造实列的对象。
第三个参数是可选的。它的作用是拦截对象属性。
该方法是拦截函数的调用的。该方法接收三个参数,分别是目标对象。目标对象上下文this对象 和 目标对象的数组;它和 Reflect.apply参数是一样的
第三个参数是可选的。它的作用是拦截对象属性。
写在最后的话大家不要忘记,点赞,评论,收藏