readonly 的处理
首先响应式代码的函数我们就可以做出相应的修改,创建核心函数createReactiveObject
,然后通过高阶函数在外层通过参数进行处理。
reactive.ts
// 为了区分普通代理reactiveMap与只读readonlyMap分开进行存储
export const reactiveMap = new WeakMap<Target, any>();
export const readonlyMap = new WeakMap<Target, any>();
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>
) {
// 如果不是对象,直接返回
if (!isObject(target)) {
console.error("target is not object");
return target;
}
// 如果已经代理过了,就不要再代理了
const proxyMap = isReadonly ? readonlyMap : reactiveMap;
const existingProxy = proxyMap.get(target);
if (existingProxy) {
return existingProxy;
}
// 只要读到了target[ReactiveFlags.IS_REACTIVE],就返回target
// 因为Proxy对象直接拦截了这个属性
if (target[ReactiveFlags.RAW] && target[ReactiveFlags.IS_REACTIVE]) {
return target;
}
const proxy = new Proxy(target, baseHandlers);
proxyMap.set(target, proxy);
return proxy;
}
之前的 reactive 函数,其实只需要调用这个函数就行了
export function reactive<T extends object>(target: T): T;
export function reactive(target: object) {
// 如果已经是只读代理,直接返回
if (target && (target as Target)[ReactiveFlags.IS_READONLY]) {
return target;
}
// 第二个参数false,表示不是只读
return createReactiveObject(target, false, mutableHandlers);
}
创建readonly
代理
// 使用TS的递归实现深层的readonly
// 比较实用的类型体操技能,可以帮我们看到深层计算之后的结果
// T extends any ?
// 具体类型体操代码
// : never
type DeepReadonly<T extends Record<string, any>> = T extends any
? {
readonly [K in keyof T]: T[K] extends Record<string, any>
? DeepReadonly<T[K]>
: T[K];
}
: never;
export function readonly<T extends object>(target: T): DeepReadonly<T> {
return createReactiveObject(
target,
true,
readonlyHandlers // readonly的handler处理程序需要单独进行处理
);
}
同样,在 baseHandlers.ts 代码中,对于之前写个get()
, set()
等方法也同样可以通过高阶函数进行处理,同时我们需要加入对readonly
的处理
baseHandlers.ts
function createGetter(isReadonly = false, shallow = false) {
return function get(
target: object,
key: string | symbol,
receiver: object
): any {
// 如果访问的是ReactiveFlags.IS_REACTIVE,返回true
if (key === ReactiveFlags.IS_REACTIVE) {
return true;
}
// 如果访问的是ReactiveFlags.IS_READONLY, 返回true
else if (key === ReactiveFlags.IS_READONLY) {
return isReadonly;
} else if (
key === ReactiveFlags.RAW &&
receiver === (isReadonly ? readonlyMap : reactiveMap).get(target)
) {
return target;
}
// 只有在非只读的情况下才会收集依赖
if (!isReadonly) {
track(target, TrackOpTypes.GET, key);
}
// 传入对象如果是数组
const targetIsArray = isArray(target);
if (targetIsArray && arrayInstrumentations.hasOwnProperty(key)) {
// 对象修改之后的方法进行依赖收集
return Reflect.get(arrayInstrumentations, key, receiver);
}
// 返回对象的相应属性值,推荐使用 Reflect.get
const result = Reflect.get(target, key, receiver);
// 如果整个对象是只读的,那么这个对象的属性是对象,也应该是只读的
if (isObject(result)) {
return isReadonly ? readonly(result) : reactive(result);
}
return result;
};
}
// ......其他代码省略
const get = createGetter();
const readonlyGet = createGetter(true);
export const mutableHandlers: ProxyHandler<object> = {
get,
set,
has,
ownKeys,
deleteProperty,
};
export const readonlyHandlers: ProxyHandler<object> = {
get: readonlyGet,
set(target, key) {
console.warn(
`Set operation on key "${String(key)}" failed: target is readonly.`,
target
);
return true;
},
deleteProperty(target, key) {
console.warn(
`Delete operation on key "${String(key)}" failed: target is readonly.`,
target
);
return true;
},
};
测试:
import { readonly } from "./reactive";
const obj = {
a: 1,
b: 2,
c: {
d: 3,
},
};
const readonlyProxy = readonly(obj);
// readonly应该不触发依赖收集
readonlyProxy.a;
// 对象的直接属性不能修改
// @ts-ignore
readonlyProxy.a = 2;
console.log(readonlyProxy.a);
// 嵌套的对象的属性也不能修改
// @ts-ignore
readonlyProxy.c.d = 22;
console.log(readonlyProxy.c.d);
Last updated on