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 的更多知识我也正在学习中,不足之处还望各位大佬不吝斧正~