既然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