Reactive
的内部怎么实现响应式呢?
# Proxy
reactive
的作用其实就是在原对象外层套了一层 Proxy 对象,所以我们想要分析 reactive 的响应式,那就要先理解一下Proxy
在中间做了什么
我们可以自己先定义一个 Proxy 对象,用它来封装一下我们的原对象
先不写 Handler 的逻辑,看一下此时能实现什么功能
const person = {
name: "smeb",
age: 18,
};
const p1 = new Proxy(person, {});
发现这时候其实已经实现了数据代理的功能,我们通过修改 p1 的 name 和 age,是可以修改原数据 person 的值的
那到现在不就结束了么,其实并没有,因为我们要讨论的是响应式,现在只是实现了数据代理的功能,之所以会这样想是因为很多人会觉得数据代理就是响应式,而我们通过这样一个例子说明了一个不设置任何 Handler 的 Proxy 就可以实现数据代理了
# 响应式
响应式不是数据代理,而是数据劫持,所以要实现响应式的效果,其实就是要完成数据劫持,而要想实现它,就要依靠 Handler 了
const p = new Proxy(person, {
//有人读取p的某个属性时调用
get(target, propName) {
console.log(`读取了person身上的${propName}属性`);
return target[propName];
},
//有人修改p的某个属性、或给p追加某个属性时调用
set(target, propName, value) {
console.log(`修改了person身上的${propName}属性,需要更新界面了`);
target[propName] = value;
},
//有人删除p的某个属性时调用
deleteProperty(target, propName) {
console.log(`删除了person身上的${propName}属性,需要更新界面了`);
// return Reflect.deleteProperty(target, propName);
delete target[propName];
},
});
通过设置了 Proxy 的 Handler,类似于配置对象,get —— 读取,增加, set —— 修改,deleteProperty —— 删除
这时我们就实现了数据劫持的效果了,也就可以进一步实现了响应式了
但现在还存在一个问题,仔细看代码可以看到,我们在Handler
中实现对原数据的增删改查时,其实是在直接操作原数据,这并没有什么不对的地方,只是,我们在vue
或者其他封装好的框架中,尽量就不要直接修改原数据,因为如果我们操作时发生了 error,会直接报错,从而影响我们后面代码的运行,这在实际开发中是很严重的问题,我们可以通过对操作进行try...catch
,来捕获错误的来源,但实际操作起来是很麻烦的
# Reflect
ECMA6
之后,慢慢的尝试将Object
上的一些Api
移到了Reflect
(反射对象)上,Reflect 相比较于Object
的优点,就是它有一个返回值,如果这条语句成功执行了,就返回 true,反之返回 false
有了这个返回值,对于后面的代码,vue 就可以很轻松的进行逻辑处理了
const p = new Proxy(person, {
//有人读取p的某个属性时调用
get(target, propName) {
console.log(`读取了person身上的${propName}属性`);
return Reflect.get(target, propName);
},
//有人修改p的某个属性、或给p追加某个属性时调用
set(target, propName, value) {
console.log(`修改了person身上的${propName}属性,需要更新界面了`);
return Reflect.set(target, propName, value);
},
//有人删除p的某个属性时调用
deleteProperty(target, propName) {
console.log(`删除了person身上的${propName}属性,需要更新界面了`);
return Reflect.deleteProperty(target, propName);
},
});
综上所述,vue3
中的reactive
响应式原理底层大概就是这个逻辑了~