写在开头,我相信只要稍微是对vue源码有一定了解的人,都知道vue2.0是通过Object.defineProperty
来实现数据的劫持的,而vue3.0换了以一种数据监听的方式proxy
,毋庸置疑,proxy
相比于Object.defineProperty
肯定是有一定优势的,不然尤大大也不会花费力气重写,所以今天我们来聊一聊Object.defineProperty
和Proxy
。
Object.defineProperty的劣势
- 无法实现对数组的监听。
但是使用过vue2.0的人都知道,当数组发生改变的时候,视图也会发生改变,它是怎么实现的呢?下面为大家揭开它什么的面纱。最主要的原因是vue2.0它重写了Array.prototype
的方法,当被监听数据的类型是数组是,改变它的原型。
function updateView() {
console.log("视图要更新了");
}
// 数组的原型
const oldPrototype = Array.prototype;
// 创建一个原型指向Array.prototype的空对象,数组的新原型对象
const newPrototype = Object.create(Array.prototype);
// 数组应该存在方法,下面只是一部分
const arrayMethod = ["push", "pop", "shift", "unshift"];
arrayMethod.forEach((methodName) => {
newPrototype[methodName] = function () {
oldPrototype[methodName].call(this, ...arguments);
updateView();
};
});
在上篇文章当中,数据劫持还存在一定的缺陷,没有对数组进行监听,现在我们既然知道了原理,接下来,看看具体的代码实现:
function observer(data) {
if (isBaseType(data)) return data;
// 重写数组的原型对象
if(isArray(data)) data.__proto__ = newPrototype
Object.keys(data).forEach((key) => {
defineReactive(data, key, data[key]);
});
}
虽然这样能够实现数组的监听,但是相比于proxy
来说,还是比较麻烦的。
- 实现对象的深度监听,需要一次性递归到底。对于层级比较深的数据来说,计算量比较大。
具体怎么实现对象的深度监听,可以参考我的上篇文章。
- 无法监听新增属性/删除属性(但是vue2.0提供了另外的api,分别是Vue.set和Vue.delete)
综合上述的和其他种种原因,尤大大决定更换数据监听的方式。
Proxy
proxy是es6新增的一个api,proxy在目标对象的外层搭建了一层拦截,外界对目标对象的某些操作,必须通过这层拦截,所以我们在这一层拦截当中可以完成我们想完成的事情。
下面我们来看看proxy的具体用法,首先要生成一个Proxy实例,如下:
const proxy = new Proxy(target, handler);
target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。继续看下面的代码:
// 现在有个对象
const target = {
name: 'poetries',
};
const handler = {
get: function(target, key, receive) {
const result = Reflect.get(target, key,receive);
console.log(`${key} 被读取`,result);
return result;
},
set: function(target, key, value, receive) {
const result = Reflect.set(target, key, value, receive);
console.log(`${key} 被设置为 ${value}`);
return result;
},
deleteProperty: function(target, key){
const result = Reflect.deleteProperty(target, key);
console.log(`${key}`被删除);
return result;
}
}
从上面的代码我们可以看出,当操作完成对象的某个属性之前,我们都可以任意执行我们的业务逻辑。接下来就是利用proxy
代替Object.defineProperty
来实现数据劫持。
function observer(data) {
if (isBaseType(data)) return data;
const handler = {
get: function(target, key, receive) {
// 只处理自身上的属性(不包括原型)
const ownKeys = Reflect.ownKeys(target);
if(ownKeys.includes(key)){
console.log(`${key},你已被监听`);
}
const result = Reflect.get(target, key,receive);
console.log(`${key} 被读取`,result);
return observer(result);
},
set: function(target, key, value, receive) {
if(target[key] === value){
// 如果前后两次的value相同,则直接跳过,不做处理
return true;
}
const result = Reflect.set(target, key, value, receive);
console.log(`${key} 被设置为 ${value}`);
// 深度监听,和Object.defineProperty不同是,只有当使用这个对象才会去监听对象里面的属性。
return result;
},
deleteProperty: function(target, key){
const result = Reflect.deleteProperty(target, key);
console.log(`${key}`被删除);
return result;
}
}
const observedData = new Proxy(data,handler);
return observedData;
}
所以,proxy
很好的规避了Object.defineProperty
的问题。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!