目标
- readonly 和 reactive 相似
- 但不能 set,即说明不会触发依赖,也就不用做依赖收集 ```typescript import { readonly } from ‘../reactive’;
describe(‘readonly’, () => { it(‘happy path’, () => { // not set const original = { foo: 1, bar: { baz: 2 } }; const wrapper = readonly(original); expect(wrapper).not.toBe(original); expect(wrapper.foo).toBe(1); }); });
<a name="kRQw4"></a># 实现<a name="j3eAj"></a>## 实现 readonlyreadonly 本质就是 reactive 功能只读方法的实现```typescriptexport function readonly(raw) {return new Proxy(raw, {get(target, key) {const res = Reflect.get(target, key);return res;},set(target, key, value) {// TODO 抛出警告return true;}});}
重构优化
由于 readonly 与 reactive 很类似,可以把 get 抽离 ```typescript function createGetter (isReadonly = false) { return function get(target, key) { const res = Reflect.get(target, key);
if(!isReadonly) { track(target, key); } return res; } }
export function reactive(raw) { return new Proxy(raw, { get: createGetter(), set(target, key, value) { const res = Reflect.set(target, key, value);
trigger(target, key);return res;}
}); }
export function readonly(raw) { return new Proxy(raw, { get: createGetter(true), set(target, key, value) { // TODO 抛出警告 return true; } }); }
2. 由于编程的相对性,保持代码的一致。getter 处理完成,就可以对 setter 作处理。所以 reactive 重构为```typescriptfunction createSetter () {return function set(target, key, value) {const res = Reflect.set(target, key, value);trigger(target, key);return res;}}export function reactive(raw) {return new Proxy(raw, {get: createGetter(),set: createSetter(),});}
重构后为 reactive.ts
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 function reactive(raw) {return new Proxy(raw, {get: createGetter(),set: createSetter(),});}export function readonly(raw) {return new Proxy(raw, {get: createGetter(true),set(target, key, value) {// TODO 抛出警告return true;}});}
- 可以看出 reactive 和 readonly 的结构是相似的,可以继续地抽离至 baseHandlers ```typescript import { track, trigger } from ‘./effect’;
// 使用缓存技术,只在初始化调用一次。之后使用这个引用 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 get(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) { // TODO 抛出警告 return true; }, };
```typescriptimport { mutableHandlers, readonlyHandlers } from './baseHandlers';export function reactive(raw) {// return new Proxy(raw, mutableHandlers);return createActiveObject(raw, mutableHandlers);}export function readonly(raw) {// return new Proxy(raw, readonlyHandlers);return createActiveObject(raw, readonlyHandlers);}// 使用函数封装表达出其意图,提高可读性function createActiveObject(raw: any, baseHandlers) {return new Proxy(raw, baseHandlers);}
实现 readonly set 抛出的警告
it('warn where call set', () => {console.warn = jest.fn();const user = readonly({ age: 10 });user.age = 11;expect(console.warn).toBeCalled();});
export const readonlyHandlers = {get: readonlyGet,set(target, key, value) {console.warn(`key:${key} can not set, cause target is readonly`);return true;},};
