《Vue.js设计与实现》 读书笔记

本文最后更新于:2022年4月22日 上午

第五章 非原始值的响应方案

理解 Proxy 和 Reflect

reflect 的方法都和 proxy 是对应的,在 proxy 中使用 reflect 来进行操作可以保证 this的指向正确

const obj = {
  foo: 1,
  get bar() {
    return this.foo
  }
}

const p = new Proxy(obj, {
  get(target, key, receiver){
    tarck(target, key);
    
    return Reflect.get(target, key, receiver);
  }
})
p.bar

当我们使用代理对象p访问bar 的时候,这里的 receiver 就是 p,这样的话,barthis指向就正确了,冲洗建立起了依赖关系

JavaScript 对象及Proxy 的工作原理

需查看 ECMAScript 中的规范

以下是 阮一峰 老师的使用方式

https://es6.ruanyifeng.com/#docs/proxy

删除对象中的属性

代理 deleteProperty(target, key),并且在删除时需要 使用 has 来判断是否自身的属性,如果是且删除成功才需要触发更新,传入DELETE 类型

如何代理 Object (对象)

拦截 in 操作符

如下对 对象使用了in操作符

effect(() => {
  'foo' in obj
});

查看 ECMAScript 规范后会发现会调用对象的内部方法 [[HasProperty]],它的内部方法是has,因此可以在 Proxy中代理 has来对in操作符进行拦截,建立起依赖关系

拦截 for…in

查看文档后,发现实现最终会调用 Reflect.ownKeys(obj)来获取对象自身拥有的键,因此可以对对象在Proxy中进行ownKeys的拦截

const INERATE_KEY = Symbol();

const p = new Proxy(obj, {
  ownKeys(target){
    tarck(target, INERATE_KEY);
    
    return Reflect.ownKeys(target);
  }
})

这里需要注意的是,在进行追踪的时候,通过了INERATE_KEY来作为key进行跟踪,这是因为,getset都能够清楚的知道key名,而在for...in遍历时,没有具体的key名来作为标识,因此需要构造出一个唯一的key

因此在响应的时候也应该触发它

trigger(target, INERATE_KEY);

思考,如果给对象新增一个属性,该如何重新让福州用函数重新执行

p.bar = 2;

合理地触发响应

  • 如果新值和旧值都一致,则不需要触发更新 (在 set 时先获取一次值进行判断)

  • 避免 NaN 等情况

    (old !== newVal && (oldVal === oldVal || newVal === newVal)){
      trigger(target, key, type);
    }
  • 设置两个响应式变量(A, B),将(B)的原型设置为响应式变量A,再通过B去修改原型上的属性,会触发两次更新,(set时需要判断 (target === receiver.raw)如果相等才触发更新)

浅响应和深响应


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议,转载请注明出处。