一起学Vue3源码,实现最简Vue3【09】 - 实现 shallowReadonly 功能

实现 shallowReadonly 功能

什么是shallowReadonly?
顾名思义 — 就是只把对象表层转换成readonly类型。当项目太过庞大时,有些数据没必要全部转换响应式对象,从而实现一定的程序优化。

shallowReadonly,可以想象成 readonly 的表亲,除了没有嵌套转化对象,其余功能都是一样的,同样不可以被修改。

shallowReadonly.spec.ts

import {
    
     isReadonly, shallowReadonly } from "../src/reactive";

describe("shallowReadonly", () => {
    
    
  // 只做表层的响应式,内部对象不是响应式,节约性能
  it("should not make non-reactive properties reactive", () => {
    
    
    const props = shallowReadonly({
    
     p: {
    
     bar: 1 } });

    expect(isReadonly(props)).toBe(true);
    expect(isReadonly(props.p)).toBe(false);
  });

  it("warning when set triggered", () => {
    
    
    // mock   console.warn 警告函数调用
    console.warn = jest.fn();

    const user = shallowReadonly({
    
    
      age: 10,
    });

    user.age = 11;

    expect(console.warn).toBeCalled();
  });
});

接下来,实现他具体的逻辑。

reactive.ts

...
export function shallowReadonly(raw) {
    
    
  return createActiveObj(raw, shallowReadonlyHandlers);
}
...

这里,抽离出shallowReadonlyHandlers,然后单独实现。达到代码复用性和统一性。
shallowReadonly同样可以参考 readonly 逻辑,在创建 ge t时,用 shallow 去打上标识,当 shallow 为 true 时,不用转换嵌套对象以及依赖收集。所以直接返回即可。
然后继承 readonly 的 handlers,再改写专属的 get 方法即可,最后导出 shallowReadonlyHandlers

baseHandlers.ts

...
const shallowReadonlyGet = createGetter(true, true);

function createGetter(isReadonly: Boolean = false, shallow: Boolean = false) {
    
    
  return function get(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;

    // 判断内部属性是否是 Object,是的话继续转换成 Proxy
    if (isObject(res)) {
    
    
      return isReadonly ? readonly(res) : reactive(res);
    }

    if (!isReadonly) {
    
    
      // 收集依赖
      track(target, key);
    }
    return res;
  };
}

export const shallowReadonlyHandlers = extend({
    
    }, readonlyHandlers, {
    
    
  get: shallowReadonlyGet,
});

以上,执行测试,通过

最后

附上git代码地址:mini-vue,共勉。