目标
- 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>
## 实现 readonly
readonly 本质就是 reactive 功能只读方法的实现
```typescript
export 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 重构为
```typescript
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(),
});
}
重构后为 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; }, };
```typescript
import { 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;
},
};