实现 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,欢迎交流探讨。