『手写Vue3』嵌套、shallowReadonly和isProxy

115 阅读1分钟

这一次的内容比较轻松,首先是让我们上一节实现的isReactive、isReadonly实现嵌套的功能,然后实现shallowReadonly(不支持嵌套),最后是isProxy。

isReactive / isReadonly 嵌套

什么是嵌套呢,先来感受一下:

  it('nested reactives', () => {
    const original = {
      nested: {
        foo: 1
      },
      array: [{ bar: 2 }]
    };
    const observed = reactive(original);
    // 支持嵌套
    expect(isReactive(observed.nested)).toBe(true);
    expect(isReactive(observed.array)).toBe(true);
    expect(isReactive(observed.array[0])).toBe(true);
  });

即:如果响应式对象的某个属性值也是对象,那么使用isReactive判断这个对象时,也应该得到true。

要做到这点,只需要访问响应式对象的属性时,判断是不是对象,如果是对象,getter要先把对象用reactive或readonly包裹,然后再返回。

const isObject = (val) => {
  return val !== null && typeof val === 'object';
};

完整的createGetter函数在说完shallowReadonly后一起给出。

shallowReadonly

有些时候,出于性能考虑,只想要最外一层对象是响应式的,而且还得是readonly。此时要避免走入用reactice或readonly包裹的逻辑,而且和readonly一样不执行track。

添加shallow,用于控制上述行为。

const shallowReadonlyGet = createGetter(true, true);

function createGetter(isReadonly = false, shallow = false) {
  return function (target, key) {
    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly;
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly;
    }

    const res = Reflect.get(target, key);
    
    // shallow直接返回
    if (shallow) {
      return res;
    }
    // 依赖收集
    if (!isReadonly) {
      track(target, key);
    }

    if (isObject(res)) {
      return isReadonly ? readonly(res) : reactive(res);
    }
    return res;
  };
}

// extend就是Object.assign()
// 因为shallowReadonly和readonly的setter一样,都会抛出警告,所以复用
export const shallowReadonlyHandlers = extend({}, readonlyHandlers, {
  get: shallowReadonlyGet
});

isProxy

isProxy的逻辑十分简单:

export function isProxy(value) {
  return isReactive(value) || isReadonly(value);
}