Skip to Content
Vue 源码06.readonly处理

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