既然reactive是使用Proxy实现的,而且也可以套娃,那么我们自己定义一个Proxy套一下reactive会怎么样呢?
先不用管有没有意义,套上再说,于是有了这样的代码。
/**
* 用Proxy定义一个 reactive 的套娃。
* @param {*} _target 要拦截的目标
* @param {*} callback 属性变化后的回调函数
*/
const myReactive = (_target, callback) => {
const proxy = new Proxy(_target, {
get: function (target, key, receiver) {
if (typeof key !== 'symbol') { // 别问我为啥要加这个,因为不加会报错。
console.log(`getting ${key}!`, target[key])
} else {
console.log('getting symbol:', key, target[key])
}
// 调用原型方法
return Reflect.get(target, key, receiver)
},
set: function (target, key, value, receiver) {
console.log(`setting ${key}:${value}!`)
// 调用原型方法
return Reflect.set(target, key, value, target) // 写 receiver 的话,模板不会自动更新
}
})
// 返回实例
return proxy
}
按照网上介绍,写了这个Proxy的函数,然后满心欢喜的套上了reactive,然后绑定模板设置更新按钮。
结果更新后p反映没有。
不对呀,不是把操作交给reactive了,没有理由不刷新模板呀。
卡了好几天,后来发现自己真笨,不会设置断点跟踪一下吗?整段的源码看不懂,看个set跟踪还不懂吗?
跟了几次终于看明白了,原来set里面做了一个 target 和 receiver 的对比判断,结果就跳过去了。
于是我把 receiver 改成 target 看他还跳不跳。
结果终于刷新模板了。
好吧,套个娃确实没啥实际用处,只是一顿折腾后,对于Vue是如何实现响应性,以及如何刷新模板的有了更深一点点的了解。
另外,这个不支持子子属性的拦截。只能拦截一层属性,如果要深入拦截,还要做个递归,对每个嵌套对象都设置个Proxy才行。等等,reactive好像不是这么实现的。。。
这是模板调用的监听结果:
getting __v_isRef! undefined
getting toJSON! undefined
getting symbol: Symbol(Symbol.toStringTag) undefined
getting symbol: Symbol(Symbol.toStringTag) undefined
getting symbol: Symbol(Symbol.toStringTag) undefined
getting name! jyk
getting age! 18