实现 readonly 功能
什么是readonly?
顾名思义 – 就是只能读,不能修改。
TDD编码思维,测试 -> 实现 -> 重构代码 -> 优化
readonly.spec.ts
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(wrapped.prop).toBe(1);
});
it("warning when set triggered", () => {
// mock console.warn 警告函数调用
console.warn = jest.fn();
const user = readonly({
age: 10,
});
user.age = 11;
expect(console.warn).toBeCalled();
});
});
首先来实现第一个测试功能,readonly
reactive.ts
// 封装成通用创建 Proxy 函数
function createActiveObj(raw: any, baseHandlers) {
return new Proxy(raw, baseHandlers);
}
export function readonly(raw) {
return createActiveObj(raw, readonlyHandlers);
}
1、首先,由于都是响应对象,把readonly和reactive 抽离出公共逻辑去使用,这里就是代码重构思想,(由于图文形式,直接放重构后的代码,会一一讲解)
2、createActiveObj 就是创建个代理对象,接收两个参数,一个代理对象,一个处理代理对象的方法
readonlyHandlers 本质就是一个对象格式,所以我们直接抽离到公共文件baseHandlers内
baseHandlers.ts
import { extend, isObject } from "../../shared";
import { track, trigger } from "./effect";
import { reactive, readonly } from "./reactive";
// 缓存一下get, set 后面始终用同一个
const get = createGetter();
const set = createSetter();
const readonlyGet = createGetter(true);
// 创建 get 通用方法
function createGetter(isReadonly: Boolean = false) {
return function get(target, key) {
const res = Reflect.get(target, key);
if (!isReadonly) {
// 收集依赖
track(target, key);
}
return res;
};
}
// 创建 set 通用方法
function createSetter() {
return function set(target, key, value) {
const res = Reflect.set(target, key, value);
// 触发依赖
trigger(target, key);
return res;
};
}
// 导出 reactive 代理
export const mutableHandlers = {
get,
set,
};
// 导出 readonly 代理
export const readonlyHandlers = {
get: readonlyGet,
set(target, key, value) {
// 警告函数
console.warn(
`${String(key)} cannot be set, because target is readonly, ${target}`
);
return true;
},
};
readonlyHandlers 就是 readonly 代理,由于readonly是只读,所以就不存在依赖收集和依赖触发,所以创建 get 通用方法 createGetter 中 isReadonly 属性来去做校验,是readonly就不用依赖收集;
而后,触发set的时候返回一个警告函数,就是上面第二个测试。
// mock console.warn 警告函数调用 console.warn = jest.fn();
最后,执行 yarn test
总结
本章节相对简单,就是一个弱化的reactive,主要是代码重构方面,抽离出reactive和readonly 通用逻辑。
最后,附上git代码:mini-vue