一起学Vue3源码,实现最简Vue3【08】 - 实现 reactive 和 readonly 嵌套对象转换功能

实现 reactive 和 readonly 嵌套对象转换功能

reactive、 readonly代理的对象中还存在对象子结构,也要相应转换成proxy

还是老样子,TDD。

reactive.spec.ts

import { isProxy, isReactive, reactive } from "../src/reactive";

describe("reactive", () => {
  ...
  it("nested reactive", () => {
    // 嵌套测试
    const original = {
      nestedObj: {
        foo: 1,
      },
      array: [{ bar: 2 }],
    };
    const observed = reactive(original);

    expect(isReactive(observed.nestedObj)).toBe(true);
    expect(isReactive(observed.array)).toBe(true);
    expect(isReactive(observed.array[0])).toBe(true);
  });
});

readonly.spec.ts

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

describe("readonly", () => {
  it("happy path", () => {
    // set not be triggered
    const original = { prop: 1, bar: { age: 10 } };
    const wrapped = readonly(original);
    expect(wrapped).not.toBe(original);

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

    // 嵌套测试
    expect(isReadonly(wrapped.bar)).toBe(true);
    expect(isReadonly(original.bar)).toBe(false);

    expect(wrapped.prop).toBe(1);
  });
});

上一章节讲到 reactive 和 readonly 通用逻辑提取到 baseHandlers 文件中

baseHandlers.ts

import { extend, isObject } from "../../shared";
import { track, trigger } from "./effect";
import { reactive, ReactiveFlags, readonly } from "./reactive";

// 缓存一下get, set 后面始终用同一个
const get = createGetter();
const set = createSetter();
const readonlyGet = createGetter(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);

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

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

function createSetter() {
  return function set(target, key, value) {
    const res = Reflect.set(target, key, value);

    // 触发依赖
    trigger(target, key);
    return res;
  };
}

export const mutableHandlers = {
  get,
  set,
};

export const readonlyHandlers = {
  get: readonlyGet,
  set(target, key, value) {
    // 警告函数
    console.warn(
      `${String(key)} cannot be set, because target is readonly, ${target}`
    );
    return true;
  },
};

测试一下,通过

最后

附上全部文章代码地址:mini-vue,欢迎交流探讨。

你可能感兴趣的:(一起学Vue3源码,实现最简Vue3,javascript,前端,typescript)