手撸mini-vue之isReactive和isReadonly

114 阅读1分钟

isReactive

单元测试
// reactive.spec.ts

describe("reactive", () => {
  it("happy path", () => {
    const original = { foo: 1 };
    const observed = reactive(original);
    
    expect(isReactive(observed)).toBe(true);
    expect(isReactive(original)).toBe(false);
  });
});

分析:

  • 创建一个 reactive 其实就是返回代理对象
  • 那么怎么知道代理对象是不是 reactive
  • 通过 get 操作的 isReadonly 变量可以知道当前对象是什么类型
// baseHandlers.ts

function createGetter(isReadonly = false) {
  return function get(target, key) {
    ...
  };
}

目标:当调用 isReactive 时触发 get

实现代码
// baseHandlers.ts

function createGetter(isReadonly = false) {
  return function get(target, key) {
    const res = Reflect.get(target, key);

    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly;
    }

    ...
  };
}
// reactive.ts

export const enum RactiveFlags {
  IS_REACTIVE = "__v_isReactive",
  IS_READONLY = "__v_isReadonly",
}

export function isReactive(value) {
  return !!value[ReactiveFlags.IS_REACTIVE];
}

如果 value 值不是一个 proxy, 那么不会调用 baseHandler 里的 get 方法,然后本身身上没有挂载这个key,所以得到的值会是一个 undefined, 所以这里返回值要转换成布尔值、

isReadonly

单元测试
// readonly.spce.ts

describe("readonly", () => {
  it("happy path", () => {
    const original = { foo: 1, bar: { baz: 2 } };
    const wrapped = readonly(original);

    expect(isReadonly(wrapped)).toBe(true);
    expect(isReadonly(original)).toBe(false);
  });
});

思路和 isReactive 一样,直接上实现代码

实现代码
// baseHandlers.ts

function createGetter(isReadonly = false) {
  return function get(target, key) {
    const res = Reflect.get(target, key);

    if (key === ReactiveFlags.IS_REACTIVE) {
      return !isReadonly;
    } else if (key === ReactiveFlags.IS_READONLY) {
      return isReadonly;
    }

    ...
  };
}
// reactive.ts

export const enum ReactiveFlags {
  IS_REACTIVE = "__v_isReactive",
  IS_READONLY = "__v_isReadonly",
}

export function isReadonly(value) {
  return !!value[ReactiveFlags.IS_READONLY];
}

源码地址戳这里