一起学Vue3源码,实现最简Vue3【07】 - 实现 isReactive 和 isReadonly

实现 isReactive 和 isReadonly

什么是isReactive,isReadonly?
即判断一个对象是否是reactive或者readonly

reactive.spec.ts

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

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

    expect(isReactive(observed)).toBe(true);
    expect(isReactive(original)).toBe(false);

    expect(observed.foo).toBe(1);
  });
  ...
});

readonly.spec.ts

import { 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(wrapped.prop).toBe(1);
  });
  ...
});

reactive.ts

// 创建一个枚举标识
export const enum ReactiveFlags {
  IS_REACTIVE = "__v_isReactive",
  IS_READONLY = "__v_isReadonly",
}

// 1、只要取值就会触发 get,然后在 get 中根据 内置 isReadonly 属性去判断是 Reactive or Readonly
// 2、方法只返回 true or false, 不关心是否是响应式对象,强制转换成 Boolean 类型
export function isReactive(value) {
  return !!value[ReactiveFlags.IS_REACTIVE];
}

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

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) {
    // 调用 isReadonly、isReactive触发get,通过key和固有的枚举值比较,继而看是reactive 还是 readonly
    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;
  },
};

测试一下,ok,通过

最后

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值