vue3 中的响应式原理的核心是通过 Proxy(代理),拦截对目标对象的任意属性的任意操作,诸如增删改查等,有 13 种。new Proxy() 传入两个参数,第一个是要代理的目标对象 target,第二个是 handler 对象,其属性就是对应的 13 个函数,用于定义在执行各种操作时,new 出来的这个 Proxy 对象的行为。
下面我们自己手写实现一个 shallowReactive,主要是拦截功能的这部分:
function shallowReactive(target) {
if (target && typeof target === 'object') {
return new Proxy(target, handler)
} else {
return target
}
}
注意:if 的判断里之所以要加上 target &&,是因为用 typeof 遇到 null 得到的结果也是 object。
handler 对象:
const handler = {
get(target, prop) {
console.log('读取')
return Reflect.get(target, prop)
},
set(target, prop, value) {
console.log('修改')
return Reflect.set(target, prop, value)
},
deleteProperty(target, prop) {
console.log('删除')
return Reflect.deleteProperty(target, prop)
}
}
我们可以新建一个普通对象 obj 进行测试:
const obj = {
name: 'Jay',
newAlbum: {
time: '很快'
}
}
把 obj 传给我们自己定义的 shallowReactive,将返回值赋给 proxyObj,然后对其进行了 += 的操作,也就是进行了“查”和“改”:
const proxyObj = shallowReactive(obj)
proxyObj.name += 'Chou'
现在打印代理对象 console.log(proxyObj) 得到结果如下:
打印目标对象 console.log(obj):
可以看到代理对象和目标对象的 name 都被修改了,且打印出了“读取”和“修改”,证明 handler 对象里的 get 和 set 触发。
注意:如果在 set 或 get 函数里不调用 Reflect 的相关属性函数,比如我们把 return Reflect.get(target, prop) 和 return Reflect.set(target, prop, value) 都进行注释,那么打印结果如下:
可以看到代理对象和目标对象的 name 属性都没有被更改。
如果只注释 return Reflect.get(target, prop),结果如下:
因为不返回 Reflect.get 导致 proxyObj.name 为 undefined。
所以 handler 对象的每个属性(函数)里都需要返回相应的 Reflect 的属性(函数)。Reflect 是一个内置的对象,提供拦截 js 操作的方法。注意 Reflect 不是一个函数对象,不可构造,意味着它的所有方法都是静态方法。这些方法与 Proxy 的 handler 的方法一一对应,也是 13 种。总结一句话就是使用 Proxy 实现对对象内部数据的劫持,使用 Reflect 操作对象内部数据。
本篇到此结束,vue3 的更多知识我也正在学习中,不足之处还望各位大佬不吝斧正~