问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

vue3源码阅读-reactive创建响应式对象

发布网友 发布时间:2024-09-26 08:23

我来回答

1个回答

热心网友 时间:2024-10-25 03:59

如题,本文是我在阅读reactive源码实现过程的记录:

创建响应式对象:function?reactive(target:?object)?{??//?如果原始对象只读??if?(isReadonly(target))?{????return?target??}??//?创建响应式对象??return?createReactiveObject(????target,????false,????mutableHandlers,????mutableCollectionHandlers,????reactiveMap??)}function?createReactiveObject(??target:?Target,??isReadonly:?boolean,??baseHandlers:?ProxyHandler<any>,??collectionHandlers:?ProxyHandler<any>,??proxyMap:?WeakMap<Target,?any>)?{??//?非对象??if?(!isObject(target))?{????return?target??}??//?target?已经有对应的响应式对象代理,直接返回代理对象??const?existingProxy?=?proxyMap.get(target)??if?(existingProxy)?{????return?existingProxy??}??const?proxy?=?new?Proxy(????target,????//?用?reactive?创建的响应式对象?targetType?===?TargetType.COLLECTION?的结果是?false????targetType?===?TargetType.COLLECTION???collectionHandlers?:?baseHandlers??)??//?存储代理对象??proxyMap.set(target,?proxy)??//?返回代理对象??return?proxy}

从上面代码可以看出reactive是通过调用createReactiveObject函数来创建响应式对象的,它的执行过程如下:

判断target是不是一个对象,如果不是直接返回target。

判断proxyMap中是否已经存在target对象的代理对象,如果有直接返回target对象。

通过newProxy对target配置代理,返回代理对象并赋值给变量proxy。

将代理对象proxy存入proxyMap。

返回proxy对象,我们在业务代码实际访问到的就是它。

proxyMap:它是一个WeakMap,在reactive创建响应式对象的过程中起到了性能优化的作用。通过proxyMap.get(target)获取已经创建好的proxy,如果有的话,就不需要再次创建了。WeakMap?持有的是每个键对象的“弱引用”,这意味着在没有其他引用存在时垃圾回收能正确进行WeakMap说明

追踪依赖项function?createReactiveObject(??target:?Target,??isReadonly:?boolean,??baseHandlers:?ProxyHandler<any>,??collectionHandlers:?ProxyHandler<any>,??proxyMap:?WeakMap<Target,?any>)?{??const?proxy?=?new?Proxy(????target,????//?用?reactive?创建的响应式对象?targetType?===?TargetType.COLLECTION?的结果是?false????targetType?===?TargetType.COLLECTION???collectionHandlers?:?baseHandlers??)??//?返回代理对象??return?proxy}

上面这段代码只保留了createReactiveObject中将target包装成响应式对象的代码,响应式的实现都包含在Proxy的handler参数里,这儿对应的就是baseHandlers。

const?get?=?/*#__PURE__*/?createGetter()//?basehandlersconst?mutableHandlers:?ProxyHandler<object>?=?{??//?get?操作,用来获取?reactive?对象上的值,?需要跟踪的依赖项包含在这个函数里面。??get,??//?set?操作,用来更新?reactive?对象上的值,触发更新包含在这个函数里面。??set,??deleteProperty,??has,??ownKeys}

从上面代码能够看出来,basehandlers.get就是createGetter函数的返回值,下面开始分析这个函数的实现

function?createGetter(isReadonly?=?false,?shallow?=?false)?{??return?function?get(target:?Target,?key:?string?|?symbol,?receiver:?object)?{????const?targetIsArray?=?isArray(target)????if?(!isReadonly?&&?targetIsArray?&&?hasOwn(arrayInstrumentations,?key))?{??????return?Reflect.get(arrayInstrumentations,?key,?receiver)????}????//?获取实际的值?????const?res?=?Reflect.get(target,?key,?receiver)????//?不是只读属性????if?(!isReadonly)?{??????//?在这个函数里面收集依赖??????track(target,?TrackOpTypes.GET,?key)????}????//?res?是一个?ref????if?(isRef(res))?{??????//?ref?unwrapping?-?does?not?apply?for?Array?+?integer?key.??????const?shouldUnwrap?=?!targetIsArray?||?!isIntegerKey(key)??????return?shouldUnwrap???res.value?:?res????}????//?res?是一个对象????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.??????//?不是只读的话,将?res?也包装成一个响应式对象??????return?isReadonly???readonly(res)?:?reactive(res)????}????return?res??}}

收集依赖的入口就在track函数,下面分析这个函数的具体实现:

function?track(target:?object,?type:?TrackOpTypes,?key:?unknown)?{??//?默认这个判断通过??if?(shouldTrack?&&?activeEffect)?{????let?depsMap?=?targetMap.get(target)????if?(!depsMap)?{??????targetMap.set(target,?(depsMap?=?new?Map()))????}????let?dep?=?depsMap.get(key)????if?(!dep)?{??????depsMap.set(key,?(dep?=?createDep()))????}????const?eventInfo?=?__DEV__????????{?effect:?activeEffect,?target,?type,?key?}??????:?undefined????trackEffects(dep,?eventInfo)??}}

变量介绍:

targetMap:一个全局变量,用来存储target对应的依赖的集合,后续触发更新也会用到它。

depsMap:target对应的依赖集合,里面包含了与key对应的dep集合。

dep:用来收集依赖的集合。

targetMap的结构图示:track函数的执行过程也是比较清晰的:

通过targetMap.get(target)获取target对应的依赖集合,并赋值给depsMap。

如果depsMap不存在,执行targetMap.set(target,(depsMap=newMap())),构造一个空的map对象赋值给depsMap变量,存储到targetMap的target键上。

执行depsMap.get(key)获取depsMap上key对应Dep对象,如果不存在,调用createDep构造一个Dep对象,赋值给dep变量,存储到depsMap的key键上。

调用trackEffects追踪依赖项。

不管是reactive还是ref它们的依赖收集都是在trackEffects中完成的

export?function?trackEffects(??dep:?Dep,??debuggerEventExtraInfo?:?DebuggerEventExtraInfo)?{??let?shouldTrack?=?false??if?(effectTrackDepth?<=?maxMarkerBits)?{????if?(!newTracked(dep))?{??????dep.n?|=?trackOpBit?//?set?newly?tracked??????shouldTrack?=?!wasTracked(dep)????}??}?else?{????//?Full?cleanup?mode.????shouldTrack?=?!dep.has(activeEffect!)??}??if?(shouldTrack)?{????dep.add(activeEffect!)????activeEffect!.deps.push(dep)??}}通知依赖项更新

我们对响应式对象的属性重新赋值的时候会触发页面更新、computed或者watch重新计算或者执行,这里的核心其实就是通知依赖项更新的过程。通知更新的入口就在createSetter的返回函数中:

const?set?=?/*#__PURE__*/?createSetter()function?createSetter(shallow?=?false)?{??return?function?set(????target:?object,????key:?string?|?symbol,????value:?unknown,????receiver:?object??):?boolean?{????//?旧值????let?oldValue?=?(target?as?any)[key]????if?(isReadonly(oldValue)?&&?isRef(oldValue)?&&?!isRef(value))?{??????return?false????}????if?(!shallow?&&?!isReadonly(value))?{??????if?(!isShallow(value))?{????????value?=?toRaw(value)????????oldValue?=?toRaw(oldValue)??????}??????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????}????//?是否拥有传入的?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????if?(target?===?toRaw(receiver))?{??????//?通知依赖更新??????if?(!hadKey)?{????????trigger(target,?TriggerOpTypes.ADD,?key,?value)??????}?else?if?(hasChanged(value,?oldValue))?{????????trigger(target,?TriggerOpTypes.SET,?key,?value,?oldValue)??????}????}????return?result??}}

通过hadkey判断target是否拥有传入的属性,这里会忽略掉target原型上的属性,如果是操作原型则不会后续触发更新流程。通知依赖更新的入口函数就是tigger函数

export?function?trigger(??target:?object,??type:?TriggerOpTypes,??key?:?unknown,??newValue?:?unknown,??oldValue?:?unknown,??oldTarget?:?Map<unknown,?unknown>?|?Set<unknown>)?{??//?target?对应的依赖集合??const?depsMap?=?targetMap.get(target)??if?(!depsMap)?{????//?never?been?tracked????return??}??let?deps:?(Dep?|?undefined)[]?=?[]?//?是数组?if?(key?===?'length'?&&?isArray(target))?{????depsMap.forEach((dep,?key)?=>?{??????if?(key?===?'length'?||?key?>=?(newValue?as?number))?{????????deps.push(dep)??????}????})??}?else?{????//?schedule?runs?for?SET?|?ADD?|?DELETE????if?(key?!==?void?0)?{??????//?将?despMap?中?key?对应的?dep?集合取出??????deps.push(depsMap.get(key))????}????//?添加?iterate?到?deps?中????switch?(type)?{??????case?TriggerOpTypes.ADD:????????if?(!isArray(target))?{??????????deps.push(depsMap.get(ITERATE_KEY))??????????if?(isMap(target))?{????????????deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))??????????}????????}?else?if?(isIntegerKey(key))?{??????????//?new?index?added?to?array?->?length?changes??????????deps.push(depsMap.get('length'))????????}????????break??????case?TriggerOpTypes.DELETE:????????if?(!isArray(target))?{??????????deps.push(depsMap.get(ITERATE_KEY))??????????if?(isMap(target))?{????????????deps.push(depsMap.get(MAP_KEY_ITERATE_KEY))??????????}????????}????????break??????case?TriggerOpTypes.SET:????????if?(isMap(target))?{??????????deps.push(depsMap.get(ITERATE_KEY))????????}????????break????}??}??//?通知?deps?收集到的所有依赖项更新???if?(deps.length?===?1)?{????if?(deps[0])?{???????triggerEffects(deps[0])????}??}?else?{?????//?正常情况下不会进入else,省略这部分代码??}?}export?function?triggerEffects(??dep:?Dep?|?ReactiveEffect[],??debuggerEventExtraInfo?:?DebuggerEventExtraInfo)?{??//?spread?into?array?for?stabilization??const?effects?=?isArray(dep)???dep?:?[...dep]??for?(const?effect?of?effects)?{????if?(effect.computed)?{??????triggerEffect(effect,?debuggerEventExtraInfo)????}??}??for?(const?effect?of?effects)?{????if?(!effect.computed)?{??????triggerEffect(effect,?debuggerEventExtraInfo)????}??}}function?triggerEffect(??effect:?ReactiveEffect,??debuggerEventExtraInfo?:?DebuggerEventExtraInfo)?{??if?(effect?!==?activeEffect?||?effect.allowRecurse)?{????if?(__DEV__?&&?effect.onTrigger)?{??????effect.onTrigger(extend({?effect?},?debuggerEventExtraInfo))????}????if?(effect.scheduler)?{??????effect.scheduler()????}?else?{??????effect.run()????}??}}

trigger函数的逻辑很清晰:

targetMap.get(target)获取target对应的dep集合。

执行deps.push(depsMap.get(key))将key对应的dep集合push到deps数组。

调用triggerEffects遍历dep集合通知对应依赖更新。

原文:https://juejin.cn/post/7095640961115488269

声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
怎样移动手机应用的位置 不戴安全套但没有射精会怀孕吗 青岛哪所学校空乘专业就业率高 属牛的是什么星座1985年 m.xzw.com m.xingzuo360.cn 敏卡·凯利基本信息 想问一下eve用什么船刷古斯塔斯怪比较好?如果能有配置图就更好了... EVE加达里恐惧古斯塔斯小型控制塔一个月要多少燃料块 eve古斯塔斯据点需要打建筑么? 对煮饭手艺好的 应该用什么词语修饰 去哪里搜上海中小学教师招聘信息呢,怎么找都找不到呀,大家来帮帮忙吧... 老汉捡到长满白发的“怪石”,专家看后立马报警,结果怎么样了? 一老头捡到“长白发”的石头,专家检测后,即刻封锁整个村子,是何... ...七岁读一年级可以吗?孩子在休学期间,父母应该怎么做呢? 京东买的iphone可以到线下售后吗 锦州铁合金厂电铝热法生产钛铁是真的吗? 2月11号是什么星座的 jm珍珠防晒喷雾怎么用 专柜价格是多少 jm珍珠防晒喷雾多少钱 jm防晒喷雾可以防晒几小时 耿马附近哪里有后期制作培训学校? 耿马附近哪里有DOM培训班在哪里? 耿马附近的交互设计培训学校在哪里? 耿马附近哪里有Bootstrap培训? 耿马附近哪里有设计培训中心在哪里? 耿马附近哪里有Bootstrap培训学校哪家比较正规? 耿马附近哪里有Bootstrap培训班哪家口碑好? 耿马北大青鸟包装设计培训怎么收费? 耿马附近哪里有iOS开发培训中心在哪里? 临沂产菜架竹吗 初一上册英语单词 初一上册的英语单词 卸妆棉排行榜10强,好用的卸妆棉推荐 源码视角,Vue3为什么推荐使用ref而不是reactive Vue3响应式数组使用ref还是reactive? vue3中有没有reactive能做而ref做不了的场景? 求BEGIN的SMILE(一五一会バージョン) 求rainbow sunshine 分配歌词 丹阳到宜兴长途车 宜兴到丹阳汽车票多少钱? 从丹阳的界牌到宜兴怎么坐车才是最快的,我有急事的 宜兴到丹阳现在从哪个车站上车最早几点的 什么是金陵十二钗 红楼十二钗是哪十二钗?她们的最后结局是什么? 单反机尼康D60(18-55mm镜头)和佳能EOS 500D(18-55mm IS镜头)那个比较好... 单反相机的镜头分auto卡口和ai卡口? ...右脚大拇指被砸骨折了 我这算不算是工伤? 需要不需要做伤残鉴定?我... ...是有点肿,也没人看见,就是和旁人说了下脚被砸了,自己以为没什么事过... 大脚趾在工地被砸黑了干不活算工伤吗? 我爸上班脚被砸了导致骨裂,干不了活了,一干活就疼,,能算工伤吗,能要求... 可以给我&lt;&lt;借东西的小矮人亚莉亚蒂&gt;&gt; 粤语吗? yusian@hotmail.com_百度... 龟头边缘紫色是什么情况 龟头为什么是青紫色的