Vue3?源碼分析reactive?readonly實例
引言
上次一起閱讀了watch和computed的源碼,其實應該先看副作用effect,因為各個響應式的API里基本都用到了,等結束了reactive和readonly和ref,就一起看看effect。這次要說的是reactive和readonly,兩者在實現上流程大體一致。尤其是對Map和Set的方法的代理攔截,多少有點妙。
一、reactive 和 readonly
Vue3使用Proxy來替代Vue2中Object.defineProperty。
const target = { name: 'onlyy~' } // 創(chuàng)建一個對target的代理 const proxy = new Proxy(target, { // ...各種handler,例如get,set... get(target, property, receiver){ // 其它操作 // ... return Reflect.get(target, property, receiver) } })
1. reactive相關類型
reactive利用Proxy來定義一個響應式對象。
- Target:目標對象,包含幾個標志,以及__v_raw字段,該字段表示它原本的非響應式狀態(tài)的值;
export interface Target { [ReactiveFlags.SKIP]?: boolean [ReactiveFlags.IS_REACTIVE]?: boolean [ReactiveFlags.IS_READONLY]?: boolean [ReactiveFlags.IS_SHALLOW]?: boolean [ReactiveFlags.RAW]?: any } export const reactiveMap = new WeakMap<Target, any>() export const shallowReactiveMap = new WeakMap<Target, any>() export const readonlyMap = new WeakMap<Target, any>() export const shallowReadonlyMap = new WeakMap<Target, any>() const enum TargetType { INVALID = 0, COMMON = 1, COLLECTION = 2 }
2. 相關全局變量與方法
- ReactiveFlags:定義了各種標志對應的字符串(作為reactive對象的屬性)的枚舉;
- reactiveMap
- shallowReactiveMap
- readonlyMap
- shallowReadonlyMap:這幾個Map分別用于存放對應API生成的響應式對象(以目標對象為key,代理對象為value),便于后續(xù)判斷某個對象是否存在已創(chuàng)建的響應式對象;
- TargetType:枚舉成員的內容分別用于區(qū)分代理目標是否校驗合法、普通對象、Set或Map;
// 各個標志枚舉 export const enum ReactiveFlags { SKIP = '__v_skip', IS_REACTIVE = '__v_isReactive', IS_READONLY = '__v_isReadonly', IS_SHALLOW = '__v_isShallow', RAW = '__v_raw' } // ... export const reactiveMap = new WeakMap<Target, any>() export const shallowReactiveMap = new WeakMap<Target, any>() export const readonlyMap = new WeakMap<Target, any>() export const shallowReadonlyMap = new WeakMap<Target, any>() const enum TargetType { INVALID = 0, COMMON = 1, COLLECTION = 2 }
然后是兩個函數:targetTypeMap用于判斷各種JS類型屬于TargetType中的哪種;getTargetType用于獲取target對應的TargetType類型。
function targetTypeMap(rawType: string) { switch (rawType) { case 'Object': case 'Array': return TargetType.COMMON case 'Map': case 'Set': case 'WeakMap': case 'WeakSet': return TargetType.COLLECTION default: return TargetType.INVALID } } function getTargetType(value: Target) { return value[ReactiveFlags.SKIP] || !Object.isExtensible(value) ? TargetType.INVALID : targetTypeMap(toRawType(value)) }
3. reactive函數
reactive入參類型為object,返回值類型是UnwrapNestedRefs,對嵌套的Ref進行了解包。意味著即使reactive接收一個Ref,其返回值也不用再像Ref那樣通過.value來讀取值。源碼的注釋中也給出了示例。
/* * const count = ref(0) * const obj = reactive({ * count * }) * * obj.count++ * obj.count // -> 1 * count.value // -> 1 */
reactive內部調用createReactiveObject來創(chuàng)建響應式對象。瞄一眼入參有五個:
- target:代理目標;
- false:對應createReactiveObject的isReadonly參數;
- mutableHandlers:普通對象和數組的代理處理程序;
- mutableCollectionHandlers:Set和Map的代理處理程序;
- reactiveMap:之前定義的全局變量,收集reactive對應的依賴。
export function reactive<T extends object>(target: T): UnwrapNestedRefs<T> export function reactive(target: object) { // if trying to observe a readonly proxy, return the readonly version. if (isReadonly(target)) { return target } return createReactiveObject( target, false, mutableHandlers, mutableCollectionHandlers, reactiveMap ) }
4. 造物主createReactiveObject
不論是reactive,還是shallowReactive、readonly和shallowReadonly,都是內部調用createReactiveObject來創(chuàng)建代理的。createReactiveObject也沒什么操作,主要判斷了下target的類型,再決定是直接返回target還是返回一個新建的proxy。
以下情況直接返回target:
- target不是對象;
- target已經是一個響應式的對象,即由createReactiveObject創(chuàng)建的proxy;
- target類型校驗不合法,例如RegExp、Date等;
當參數proxyMap對應的實參(可能為reactiveMap、shallowReactiveMap、readonlyMap或shallowReadonlyMap,分別對應ractive、shallowReactive、readonly和shallowReadonly四個API)里已經存在了target的響應式對象時,直接取出并返回該響應式對象;
否則,創(chuàng)建一個target的響應式對象proxy,將proxy加入到proxyMap中,然后返回該proxy。
function createReactiveObject( target: Target, isReadonly: boolean, baseHandlers: ProxyHandler<any>, collectionHandlers: ProxyHandler<any>, proxyMap: WeakMap<Target, any> ) { if (!isObject(target)) { if (__DEV__) { console.warn(`value cannot be made reactive: ${String(target)}`) } return target } // target is already a Proxy, return it. // exception: calling readonly() on a reactive object if ( target[ReactiveFlags.RAW] && !(isReadonly && target[ReactiveFlags.IS_REACTIVE]) ) { return target } // target already has corresponding Proxy const existingProxy = proxyMap.get(target) if (existingProxy) { return existingProxy } // only specific value types can be observed. const targetType = getTargetType(target) if (targetType === TargetType.INVALID) { return target } const proxy = new Proxy( target, targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers ) proxyMap.set(target, proxy) return proxy }
我們知道,代理的重點其實在與代理的處理程序,createReactiveObject根據普通對象和數組類型、Set和Map類型來區(qū)分baseHandlers和collectionHandlers。
5. shallowReactive、readonly和shallowReadonly
事實上,ractive、shallowReactive、readonly和shallowReadonly這幾個函數形式上基本一致,都是通過createReactiveObject來創(chuàng)建響應式對象,存儲在對應的proxyMap里,但是對應的baseHandlers和collectionHandlers有區(qū)別。
// shallowReactive export function shallowReactive<T extends object>( target: T ): ShallowReactive<T> { return createReactiveObject( target, false, shallowReactiveHandlers, shallowCollectionHandlers, shallowReactiveMap ) } // raedonly // 注意readonly不是響應式的,而是一個原對象的只讀的拷貝 // 具體實現在對應的handlers里 export function readonly<T extends object>( target: T ): DeepReadonly<UnwrapNestedRefs<T>> { return createReactiveObject( target, true, readonlyHandlers, readonlyCollectionHandlers, readonlyMap ) } // shallowReadonly // 是響應式的 // 只有最外層是只讀的 export function shallowReadonly<T extends object>(target: T): Readonly<T> { return createReactiveObject( target, true, shallowReadonlyHandlers, shallowReadonlyCollectionHandlers, shallowReadonlyMap ) }
事實上,ractive、shallowReactive、readonly和shallowReadonly這幾個函數形式上基本一致,都是通過createReactiveObject來創(chuàng)建響應式對象,存儲在對應的proxyMap里,但是對應的baseHandlers和collectionHandlers有區(qū)別。那么我們就知道了,其實重點都在各種handlers里。
二、對應的 Handlers
baseHandlers用于普通對象和數組的代理,collectionHandlers用于Set、Map等的代理。對應ractive、shallowReactive、readonly和shallowReadonly四個API,每一個都有自己的baseHandlers和collectionHandlers。
1. baseHandlers
在packages/reactivity/src/baseHandlers.ts文件中。分別導出了這4個API對應的baseHandlers。
1.1 reactive
reactive的baseHandlers中有5個代理程序。
// reactive export const mutableHandlers: ProxyHandler<object> = { get, set, deleteProperty, has, ownKeys }
在攔截過程中,在get、has和ownKey這幾個訪問程序中進行依賴捕獲(track),在set和deleteProperty這倆用于更改的程序中觸發(fā)更新(trigger) 。
get和set分別由函數createGetter和createSetter創(chuàng)建,這倆函數根據入參的不同,返回不同的get和set,readonly等API的baseHandlers中的get和set也大都源于此,除了兩種readonly中用于告警的set。
(1) get
createGetter兩個入參:isReadonly和isShallow,兩兩組合正好對應四個API。
- shallow:為true時不會進入遞歸環(huán)節(jié),因此是淺層的處理;
- isReadonly:在createGetter中影響proxyMap的選擇和遞歸時API的選擇,它主要發(fā)揮作用是在set中。
function createGetter(isReadonly = false, shallow = false) { return function get(target: Target, key: string | symbol, receiver: object) { // 以下幾個if分支判斷target是否已經是由這幾個API創(chuàng)建的代理對象,代理得到的proxy才具有這些key if (key === ReactiveFlags.IS_REACTIVE) { // 是否是響應式對象 return !isReadonly } else if (key === ReactiveFlags.IS_READONLY) { // 是否是只讀對象 return isReadonly } else if (key === ReactiveFlags.IS_SHALLOW) { // 是否是淺層的 響應式/只讀 對象 return shallow } else if ( // __v_raw 屬性對應 代理對象的目標對象 // 當該屬性有值,且在相應的proxyMap中存在代理對象時,說明target已經是一個proxy了 // __v_raw 屬性對應的值為target本身 key === ReactiveFlags.RAW && receiver === (isReadonly ? shallow ? shallowReadonlyMap : readonlyMap : shallow ? shallowReactiveMap : reactiveMap ).get(target) ) { return target } const targetIsArray = isArray(target) // 對數組的幾個方法進行代理,在'includes', 'indexOf', 'lastIndexOf'等方法中進行track捕獲依賴 if (!isReadonly && targetIsArray && hasOwn(arrayInstrumentations, key)) { return Reflect.get(arrayInstrumentations, key, receiver) } const res = Reflect.get(target, key, receiver) if (isSymbol(key) ? builtInSymbols.has(key) : isNonTrackableKeys(key)) { return res } // 如果不是readonly,則捕獲依賴,因此,readonly 為非響應式的 if (!isReadonly) { track(target, TrackOpTypes.GET, key) } if (shallow) { return res } // 如果get到的值是一個Ref,會直接解包,無需再使用 .value 來獲取真正需要的值 // 除非目標對象target是數組,或者當前的key是整數 // 例如,obj[0],即使是一個Ref也不會直接解包,使用的時候依然要 obj[0].value // shallow沒有走到這一步,因此也不會自動解包 if (isRef(res)) { // ref unwrapping - skip unwrap for Array + integer key. return targetIsArray && isIntegerKey(key) ? res : res.value } // 當get到的值是對象時,根據是否是readonly來遞歸操作,需要防止對象循環(huán)引用 // shallow沒有走到這一步,因此shallow是淺層的 if (isObject(res)) { // Convert returned value into a proxy as well. we do the isObject check // here to avoid invalid value warning. Also need to lazy access readonly // and reactive here to avoid circular dependency. return isReadonly ? readonly(res) : reactive(res) } return res } }
(2) set
對于reactive,可以說最主要的任務就是在set中觸發(fā)更新,set包括 新增 和 修改 屬性值。如果當前的key對應的值是一個Ref,且其它條件滿足時,則觸發(fā)更新的操作是在Ref的內部。這些在后續(xù)講解Ref的時候會提到。
function createSetter(shallow = false) { return function set( target: object, key: string | symbol, value: unknown, receiver: object ): boolean { let oldValue = (target as any)[key] // 當前值是Readonly的Ref,而新值不是Ref時,不允許修改 if (isReadonly(oldValue) && isRef(oldValue) && !isRef(value)) { return false } // 如果是深層的修改 if (!shallow) { // 解出原本的非proxy值 if (!isShallow(value) && !isReadonly(value)) { oldValue = toRaw(oldValue) value = toRaw(value) } // 目標對象非數組,當前key的值是Ref而新值不是Ref,則通過 .value 賦值 // 在Ref內部觸發(fā)更新 if (!isArray(target) && isRef(oldValue) && !isRef(value)) { oldValue.value = value return true } } else { // 淺層模式下,忽略對象是否是響應式的 // in shallow mode, objects are set as-is regardless of reactive or not } // 然后是觸發(fā)更新的部分了 // 判斷當前key是否已經存在于target上 const hadKey = isArray(target) && isIntegerKey(key) ? Number(key) < target.length : hasOwn(target, key) const result = Reflect.set(target, key, value, receiver) // don't trigger if target is something up in the prototype chain of original // 如果是原型鏈上的字段則不會觸發(fā)更新 if (target === toRaw(receiver)) { if (!hadKey) { // 當前的key已經存在,觸發(fā)新增的更新 trigger(target, TriggerOpTypes.ADD, key, value) } else if (hasChanged(value, oldValue)) { // 當前key不存在,觸發(fā)修改的更新 trigger(target, TriggerOpTypes.SET, key, value, oldValue) } } return result } }
(3) deleteProperty
刪除操作的代理程序,和set一樣,deleteProperty攔截delete和Reflect.deleteProperty()操作,它也能觸發(fā)更新。
function deleteProperty(target: object, key: string | symbol): boolean { const hadKey = hasOwn(target, key) const oldValue = (target as any)[key] const result = Reflect.deleteProperty(target, key) // 刪除成功 且 target中原來有這個屬性時,觸發(fā)刪除的更新 if (result && hadKey) { trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue) } return result }
(4) has
has用于判斷target中是否有當前的key,攔截a in obj、with(obj){(a)}、Reflect.has等操作,屬于訪問程序,在其中進行has操作的依賴收集。
function has(target: object, key: string | symbol): boolean { const result = Reflect.has(target, key) if (!isSymbol(key) || !builtInSymbols.has(key)) { track(target, TrackOpTypes.HAS, key) } return result }
(5) ownKeys
用于獲取target所有自身擁有的key,攔截Object.getOwnPropertyNames、Object.getOwnPropertySymbols、Object.keys、Reflect.ownKeys,屬于訪問程序,在其中進行迭代的依賴收集。
function ownKeys(target: object): (string | symbol)[] { track(target, TrackOpTypes.ITERATE, isArray(target) ? 'length' : ITERATE_KEY) return Reflect.ownKeys(target) }
現在我們算是都弄明白了,對于普通對象和數組,reactive創(chuàng)建proxy,通過get、set、deleteProperty、has、ownKeys五個代理處理程序,來攔截其屬性訪問操作,在其中進行依賴收集,攔截其增刪改操作,其中觸發(fā)更新。
1.2 readonly
readonly的代理處理程序只有三個:
- get:由createGetter(true)創(chuàng)建,還記得我們上面講到的createSetter嗎?
- set
- deleteProperty:這兩個代理處理程序用于告警,畢竟readonly不可修改。
畢加思索一下createGetter(true),傳入的readonly=true,使得get中不會進行track操作來收集依賴,因而不具有響應性。
const readonlyGet = /*#__PURE__*/ createGetter(true) export const readonlyHandlers: ProxyHandler<object> = { get: readonlyGet, set(target, key) { if (__DEV__) { warn( `Set operation on key "${String(key)}" failed: target is readonly.`, target ) } return true }, deleteProperty(target, key) { if (__DEV__) { warn( `Delete operation on key "${String(key)}" failed: target is readonly.`, target ) } return true } }
1.3 shallowReactive
shallowReactive移植了reactive的baseHandlers,并且更新了get和set。具體實現也可以回顧上面說到的createGetter和createSetter。
回過頭來看看createGetter(false, true),isReadonly = false,則在get中,可以進行track依賴收集;shallow = true,則在get中不會對頂層的Ref進行解包,也不會進行遞歸操作。
而在createSetter(true)中,參數shallow幾乎只影響是否要解出原本的raw值。如果新值value不是淺層且不是只讀的,則需要解出它的原本raw值,之后才能進行賦值操作,否則我們的shallowRef將不再是淺層的了。
const shallowGet = /*#__PURE__*/ createGetter(false, true) const shallowSet = /*#__PURE__*/ createSetter(true) export const shallowReactiveHandlers = /*#__PURE__*/ extend( {}, mutableHandlers, { get: shallowGet, set: shallowSet } )
1.4 shallowReadonly
移植了readonly的baseHandlers,更新了其中的get,這個get也試試由createGetter創(chuàng)建。我們知道,readonly的baseHandlers里,除了get,另外倆都是用來攔截修改操作并告警的。
回顧一下createGetter,當isReadonly===true時,不會進行track操作來收集依賴;shallow===true時,不會對Ref進行解包,也不會走到遞歸環(huán)節(jié),即是淺層的readonly。
const shallowReadonlyGet = /*#__PURE__*/ createGetter(true, true) // Props handlers are special in the sense that it should not unwrap top-level // refs (in order to allow refs to be explicitly passed down), but should // retain the reactivity of the normal readonly object. export const shallowReadonlyHandlers = /*#__PURE__*/ extend( {}, readonlyHandlers, { get: shallowReadonlyGet } )
2. cellectionHandlers
對于Set和Map較為復雜的數據結構,他們有自己的方法,因此代理程序會有些差別。基本都是攔截它們原本的方法,然后進行track或trigger。可以看到這幾個handlers中,都只有由createInstrumentationGetter創(chuàng)建的get。
export const mutableCollectionHandlers: ProxyHandler<CollectionTypes> = { get: /*#__PURE__*/ createInstrumentationGetter(false, false) } export const shallowCollectionHandlers: ProxyHandler<CollectionTypes> = { get: /*#__PURE__*/ createInstrumentationGetter(false, true) } export const readonlyCollectionHandlers: ProxyHandler<CollectionTypes> = { get: /*#__PURE__*/ createInstrumentationGetter(true, false) } export const shallowReadonlyCollectionHandlers: ProxyHandler<CollectionTypes> = { get: /*#__PURE__*/ createInstrumentationGetter(true, true) }
1.1 createInstrumentationGetter
因為是代理Set和Map,在攔截它們的實例方法之前,對實例的訪問,即get,這個get并非Map或Set實例的get方法,而是表示對實例的訪問操作。
例如:
const map = new Map([['name', 'cc']]);
map.set('age', 18);
這里map.set()首先就是訪問map的set方法,對應的key就是字符串'set',而這一步就會被代理的get程序攔截,而真正的對方法的攔截,都在相應的instrumentations里預設好了。攔截了之后,如果key在instrumentations里存在,返回預設的方法,在其中進行track和trigger操作,否則是其它屬性/方法,直接返回即可,不會進行track和trigger。
const [ mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations ] = /* #__PURE__*/ createInstrumentations() function createInstrumentationGetter(isReadonly: boolean, shallow: boolean) { const instrumentations = shallow ? isReadonly ? shallowReadonlyInstrumentations : shallowInstrumentations : isReadonly ? readonlyInstrumentations : mutableInstrumentations return ( target: CollectionTypes, key: string | symbol, receiver: CollectionTypes ) => { if (key === ReactiveFlags.IS_REACTIVE) { return !isReadonly } else if (key === ReactiveFlags.IS_READONLY) { return isReadonly } else if (key === ReactiveFlags.RAW) { return target } return Reflect.get( hasOwn(instrumentations, key) && key in target ? instrumentations : target, key, receiver ) } }
1.2 instrumentations
和baseHandlers相比,Proxy無法直接攔截Map和Set的方法的調用,而是通過get程序來攔截,再判斷key是否為執(zhí)行增刪改查的方法,從而判斷是否進行依賴收集或更新。因此,就需要先預設好,哪些key作為方法名時可以觸發(fā)track和trigger。其實也就是Map和Set的那些實例方法和迭代器方法。而各種Instrumentations,就是這些預設的方法,track和trigger操作都在其中。
function createInstrumentations() { // 對應reactive const mutableInstrumentations: Record<string, Function> = { get(this: MapTypes, key: unknown) { return get(this, key) }, get size() { return size(this as unknown as IterableCollections) }, has, add, set, delete: deleteEntry, clear, forEach: createForEach(false, false) } // 對應shallowReactive const shallowInstrumentations: Record<string, Function> = { get(this: MapTypes, key: unknown) { return get(this, key, false, true) }, get size() { return size(this as unknown as IterableCollections) }, has, add, set, delete: deleteEntry, clear, forEach: createForEach(false, true) } // 對應readonly const readonlyInstrumentations: Record<string, Function> = { get(this: MapTypes, key: unknown) { return get(this, key, true) }, get size() { return size(this as unknown as IterableCollections, true) }, has(this: MapTypes, key: unknown) { return has.call(this, key, true) }, add: createReadonlyMethod(TriggerOpTypes.ADD), set: createReadonlyMethod(TriggerOpTypes.SET), delete: createReadonlyMethod(TriggerOpTypes.DELETE), clear: createReadonlyMethod(TriggerOpTypes.CLEAR), forEach: createForEach(true, false) } // 對應shallowReadonly const shallowReadonlyInstrumentations: Record<string, Function> = { get(this: MapTypes, key: unknown) { return get(this, key, true, true) }, get size() { return size(this as unknown as IterableCollections, true) }, has(this: MapTypes, key: unknown) { return has.call(this, key, true) }, add: createReadonlyMethod(TriggerOpTypes.ADD), set: createReadonlyMethod(TriggerOpTypes.SET), delete: createReadonlyMethod(TriggerOpTypes.DELETE), clear: createReadonlyMethod(TriggerOpTypes.CLEAR), forEach: createForEach(true, true) } // 使用 createIterableMethod 給這些 Instrumentations 掛上幾個迭代器 const iteratorMethods = ['keys', 'values', 'entries', Symbol.iterator] iteratorMethods.forEach(method => { mutableInstrumentations[method as string] = createIterableMethod( method, false, false ) readonlyInstrumentations[method as string] = createIterableMethod( method, true, false ) shallowInstrumentations[method as string] = createIterableMethod( method, false, true ) shallowReadonlyInstrumentations[method as string] = createIterableMethod( method, true, true ) }) return [ mutableInstrumentations, readonlyInstrumentations, shallowInstrumentations, shallowReadonlyInstrumentations ] }
函數createInstrumentations分為兩部分,前部分是利用已有的get、set、add、has、clear等等來得到各個instrumentations,后部分是對各個instrumentations中的迭代方法的更新。只要不是isReadonly不是真值,則無論是get、set等方法還是keys、values等迭代器接口,都在內部進行了track或trigger,當然,get、has、size等方法 和 幾個迭代器方法都屬于訪問操作,因此內部是使用track來收集依賴,而trigger發(fā)生在增、刪、改操作里,當然,也要根據isReadonly和shallow有所區(qū)分,思路基本和baseHandlers一致。
function get( target: MapTypes, key: unknown, isReadonly = false, isShallow = false ) { // #1772: readonly(reactive(Map)) should return readonly + reactive version // of the value target = (target as any)[ReactiveFlags.RAW] const rawTarget = toRaw(target) const rawKey = toRaw(key) if (!isReadonly) { if (key !== rawKey) { track(rawTarget, TrackOpTypes.GET, key) } track(rawTarget, TrackOpTypes.GET, rawKey) } const { has } = getProto(rawTarget) const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive if (has.call(rawTarget, key)) { return wrap(target.get(key)) } else if (has.call(rawTarget, rawKey)) { return wrap(target.get(rawKey)) } else if (target !== rawTarget) { // #3602 readonly(reactive(Map)) // ensure that the nested reactive `Map` can do tracking for itself target.get(key) } } function has(this: CollectionTypes, key: unknown, isReadonly = false): boolean { const target = (this as any)[ReactiveFlags.RAW] const rawTarget = toRaw(target) const rawKey = toRaw(key) if (!isReadonly) { if (key !== rawKey) { track(rawTarget, TrackOpTypes.HAS, key) } track(rawTarget, TrackOpTypes.HAS, rawKey) } return key === rawKey ? target.has(key) : target.has(key) || target.has(rawKey) } function size(target: IterableCollections, isReadonly = false) { target = (target as any)[ReactiveFlags.RAW] !isReadonly && track(toRaw(target), TrackOpTypes.ITERATE, ITERATE_KEY) return Reflect.get(target, 'size', target) } function add(this: SetTypes, value: unknown) { value = toRaw(value) const target = toRaw(this) const proto = getProto(target) const hadKey = proto.has.call(target, value) if (!hadKey) { target.add(value) trigger(target, TriggerOpTypes.ADD, value, value) } return this } function set(this: MapTypes, key: unknown, value: unknown) { value = toRaw(value) const target = toRaw(this) const { has, get } = getProto(target) let hadKey = has.call(target, key) if (!hadKey) { key = toRaw(key) hadKey = has.call(target, key) } else if (__DEV__) { checkIdentityKeys(target, has, key) } const oldValue = get.call(target, key) target.set(key, value) if (!hadKey) { trigger(target, TriggerOpTypes.ADD, key, value) } else if (hasChanged(value, oldValue)) { trigger(target, TriggerOpTypes.SET, key, value, oldValue) } return this } function deleteEntry(this: CollectionTypes, key: unknown) { const target = toRaw(this) const { has, get } = getProto(target) let hadKey = has.call(target, key) if (!hadKey) { key = toRaw(key) hadKey = has.call(target, key) } else if (__DEV__) { checkIdentityKeys(target, has, key) } const oldValue = get ? get.call(target, key) : undefined // forward the operation before queueing reactions const result = target.delete(key) if (hadKey) { trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue) } return result } function clear(this: IterableCollections) { const target = toRaw(this) const hadItems = target.size !== 0 const oldTarget = __DEV__ ? isMap(target) ? new Map(target) : new Set(target) : undefined // forward the operation before queueing reactions const result = target.clear() if (hadItems) { trigger(target, TriggerOpTypes.CLEAR, undefined, undefined, oldTarget) } return result }
1.3 createIterableMethod
這里稍微提一下createIterableMethod,用于利用Map和Set本身的迭代器方法,并做了一點修改,在其中加入了track來收集依賴。
function createIterableMethod( method: string | symbol, isReadonly: boolean, isShallow: boolean ) { return function ( this: IterableCollections, ...args: unknown[] ): Iterable & Iterator { const target = (this as any)[ReactiveFlags.RAW] const rawTarget = toRaw(target) const targetIsMap = isMap(rawTarget) const isPair = method === 'entries' || (method === Symbol.iterator && targetIsMap) const isKeyOnly = method === 'keys' && targetIsMap const innerIterator = target[method](...args) const wrap = isShallow ? toShallow : isReadonly ? toReadonly : toReactive !isReadonly && track( rawTarget, TrackOpTypes.ITERATE, isKeyOnly ? MAP_KEY_ITERATE_KEY : ITERATE_KEY ) // return a wrapped iterator which returns observed versions of the // values emitted from the real iterator return { // iterator protocol next() { const { value, done } = innerIterator.next() return done ? { value, done } : { value: isPair ? [wrap(value[0]), wrap(value[1])] : wrap(value), done } }, // iterable protocol [Symbol.iterator]() { return this } } } }
小結
分析完各個部分,可以看到,無論是baseHandlers還是collectionHandlers,思路都是一致的。
但是collectionHandlers只有get這一個代理程序,通過攔截到的key判斷是否是Map和Set實例自帶的增刪改查的方法,從而返回預設好的hack版本的方法或原本的屬性值,然后繼續(xù)后續(xù)的操作。在hack版本的方法里進行track和trigger。
以上就是Vue3 源碼分析reactive readonly實例的詳細內容,更多關于Vue3 reactive readonly的資料請關注腳本之家其它相關文章!