在 vue3 中 readonly 的作用是只读,下面通过一个单测来呈现这个功能
单测
// readonly.spec.ts
describe("readonly", () => {
it("happy path", () => {
const original = { foo: 1, bar: { baz: 2 } };
const wrapped = readonly(original);
expect(wrapped).not.toBe(original);
expect(wrapped.foo).toBe(1);
});
it("warn then call set", () => {
console.warn = jest.fn();
const user = readonly({ age: 10 });
user.age = 11;
expect(console.warn).toBeCalled();
});
});
readonly 不能 set,意味着不会触发依赖,不会触发依赖意味着不需要做依赖收集
实现代码
// reactive.ts
export function readonly(raw) {
return new Proxy(raw, {
get(target, key) {
let res = Reflect.get(target, key);
return res;
},
set(target, key, value) {
console.warn(`key:${key} set 失败 因为 target 是 readonly`);
return true;
}
})
}
代码重构
新建 baseHandlers.ts,存放 proxy handle的代码逻辑
// baseHandlers.ts
// 为了避免每次调用都创建一个 getter 或 setter
// 利用一个缓存的方法,先初始化 get,set,后续一直用初始化的get set
const get = createGetter();
const set = createSetter();
const readonlyGet = createGetter(true);
function createGetter(isReadonly = false) {
return function get(target, key) {
const res = Reflect.get(target, key);
// 依赖收集
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(`key:${key} set 失败 因为 target 是 readonly`);
return true;
},
}
// reative.ts
import { mutableHandlers, readonlyHandlers } from "./baseHandlers";
export function reactive(raw: any) {
return createActiveObject(raw, mutableHandlers);
}
export function readonly(raw: any) {
return createActiveObject(raw, readonlyHandlers);
}
function createActiveObject(raw: any, baseHandle) {
return new Proxy(raw, baseHandle);
}