用Proxy给reactive套个娃会怎么样?

226 阅读2分钟

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