You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// Make a map and return a function for checking if a key// is in that map.//// IMPORTANT: all calls of this function must be prefixed with /*#__PURE__*/// So that rollup can tree-shake them if necessary.exportfunctionmakeMap(str: string,expectsLowerCase?: boolean): (key: string)=>boolean{constmap: Record<string,boolean>=Object.create(null)constlist: Array<string>=str.split(',')for(leti=0;i<list.length;i++){map[list[i]]=true}returnexpectsLowerCase ? val=>!!map[val.toLowerCase()] : val=>!!map[val]}
makeMapk可以创建一个map存储字符串中提到的各种类型,来确定某种类型是否支持。
WeakMap
// WeakMaps that store {raw <-> observed} pairs.constrawToReactive=newWeakMap<any,any>()constreactiveToRaw=newWeakMap<any,any>()constrawToReadonly=newWeakMap<any,any>()constreadonlyToRaw=newWeakMap<any,any>()
正常情况下是需要用两个map去做双向索引的,保存raw和reactive的相互指向关系。
这里使用了WeakMap,相比map可以解决弱引用下的垃圾回收问题。
reactive
export functionreactive<Textendsobject>(target: T): UnwrapNestedRefs<T>exportfunctionreactive(target: object){// if trying to observe a readonly proxy, return the readonly version.if(readonlyToRaw.has(target)){returntarget}returncreateReactiveObject(target,rawToReactive,reactiveToRaw,mutableHandlers,mutableCollectionHandlers)}// Return a reactive-copy of the original object, where only the root level// properties are reactive, and does NOT unwrap refs nor recursively convert// returned properties.exportfunctionshallowReactive<Textendsobject>(target: T): T{returncreateReactiveObject(target,rawToReactive,reactiveToRaw,shallowReactiveHandlers,mutableCollectionHandlers)}
reactive是composition的核心api,用来创建target对象的Proxy。
reactive和shallowReactive区别在于是否是深度响应式。
通过调用createReactiveObject来创建不同的Proxy对象。
createReactiveObject
functioncreateReactiveObject(target: unknown,toProxy: WeakMap<any,any>,toRaw: WeakMap<any,any>,baseHandlers: ProxyHandler<any>,collectionHandlers: ProxyHandler<any>){if(!isObject(target)){if(__DEV__){console.warn(`value cannot be made reactive: ${String(target)}`)}returntarget}// target already has corresponding Proxyletobserved=toProxy.get(target)if(observed!==void0){returnobserved}// target is already a Proxyif(toRaw.has(target)){returntarget}// only a whitelist of value types can be observed.if(!canObserve(target)){returntarget}consthandlers=collectionTypes.has(target.constructor)
? collectionHandlers
: baseHandlersobserved=newProxy(target,handlers)toProxy.set(target,observed)toRaw.set(observed,target)returnobserved}
constget=/*#__PURE__*/createGetter()functioncreateGetter(isReadonly=false,shallow=false){returnfunctionget(target: object,key: string|symbol,receiver: object){consttargetIsArray=isArray(target)if(targetIsArray&&hasOwn(arrayInstrumentations,key)){returnReflect.get(arrayInstrumentations,key,receiver)}constres=Reflect.get(target,key,receiver)if(isSymbol(key)&&builtInSymbols.has(key)){returnres}if(shallow){!isReadonly&&track(target,TrackOpTypes.GET,key)returnres}if(isRef(res)){if(targetIsArray){!isReadonly&&track(target,TrackOpTypes.GET,key)returnres}else{// ref unwrapping, only for Objects, not for Arrays.returnres.value}}!isReadonly&&track(target,TrackOpTypes.GET,key)returnisObject(res)
? isReadonly
? // need to lazy access readonly and reactive here to avoid// circular dependencyreadonly(res)
: reactive(res)
: res}}
functioncreateSetter(shallow=false){returnfunctionset(target: object,key: string|symbol,value: unknown,receiver: object): boolean{constoldValue=(targetasany)[key]if(!shallow){value=toRaw(value)if(!isArray(target)&&isRef(oldValue)&&!isRef(value)){oldValue.value=valuereturntrue}}else{// in shallow mode, objects are set as-is regardless of reactive or not}consthadKey=hasOwn(target,key)constresult=Reflect.set(target,key,value,receiver)// don't trigger if target is something up in the prototype chain of originalif(target===toRaw(receiver)){if(!hadKey){trigger(target,TriggerOpTypes.ADD,key,value)}elseif(hasChanged(value,oldValue)){trigger(target,TriggerOpTypes.SET,key,value,oldValue)}}returnresult}}
setter相对简单很多,不用多讲。
The text was updated successfully, but these errors were encountered:
reactive
makeMap
先看个工具函数:
makeMapk可以创建一个map存储字符串中提到的各种类型,来确定某种类型是否支持。
WeakMap
正常情况下是需要用两个map去做双向索引的,保存raw和reactive的相互指向关系。
这里使用了WeakMap,相比map可以解决弱引用下的垃圾回收问题。
reactive
reactive是composition的核心api,用来创建target对象的Proxy。
reactive和shallowReactive区别在于是否是深度响应式。
通过调用createReactiveObject来创建不同的Proxy对象。
createReactiveObject
首先判断传入的target必须是一个对象,否则无法创建Proxy,基本类型使用Ref就好了。
接下来判断target的Proxy是否已经存在,以及是不是已经是个Proxy对象了。
这里使用void 0代替undefined,压缩后更短,避免undefined被重写,但是我很好奇为何不信任压缩工具嘞。
然后判断是否可以observe,只有白名单里的才可以,通常情况下没有被vue记录过,或者非vue实例、非vnode这种内部结点才可以被观测。
针对
set map weakmap weakset
使用特殊的Proxy handler,其余绝大部分场景使用baseHandler来处理proxy行为。mutableHandler
这几个
proxy handler
没有太大区别,我们直接看最常用的mutableHandlers。这个handler重写了这几个常用的属性,一个一个来看。
getter
/*#__PURE__*/
这个标志会告诉压缩工具当前的变量或者函数调用是没有副作用的,没用到的可以直接移除,实现更完善的的tree-shaking。createGetter中对数组做了特殊处理,忽略了symbol属性的依赖追踪,对Object中的元素自动展开,但是忽略数组中的元素展开。
track和trigger是用来做依赖收集和触发的,后面详细分析。
arrayInstrumentations
vue3 arrayInstrumentations解析
专门写了一下详细分析。
循环依赖
前面的都比较常规,最后这几行看一下 :
当Object对象中的某个属性仍然为object时,需要lazy处理来避免循环依赖。
解决循环依赖有几种方式,引入中间人、延迟处理、缓存等等,对Proxy来说他只能劫持第一层的getter,如果有嵌套对象(如
a.b.c
)的话,Proxy本身并不能解决问题,如果框架想直接递归遍历的话,性能和边界条件都会出问题。这里使用lazy处理直接返回一个reactive的object,访问
a.b
的时候只会触发b这一层的track;访问到a.b.c
的时候,框架会依次触发b层track,然后给b这个对象进行reactive处理,这样再访问b.c
的时候,因为已经被reactive化,此时的c
也进行了track, 就可以避开上面的两个问题。setter
setter相对简单很多,不用多讲。
The text was updated successfully, but these errors were encountered: