Vue3.0 响应式系统源码逐行分析讲解(2)

function createReactiveObject( target: any, 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)}`) } return target } let observed = toProxy.get(target) // 如果目标对象已经有proxy对象,直接返回 if (observed !== void 0) { return observed } // 如果目标对象是proxy的对象,并且有对应的真实对象,那么也直接返回 if (toRaw.has(target)) { return target } // 如果它是vnode或者vue,则不能被观测 if (!canObserve(target)) { return target } // 判断被观测的对象是否是set,weakSet,map,weakMap,根据情况使用对应proxy的,配置对象 const handlers = collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers observed = new Proxy(target, handlers) toProxy.set(target, observed) toRaw.set(observed, target) if (!targetMap.has(target)) { targetMap.set(target, new Map()) } return observed }

第一个if,判断是否是对象,否则报出警告

toProxy拿到观测对象的Proxy对象,如果存在直接返回

// 这种情况 const obj = { count: 0 } const state1 = reative(obj) const state2 = reative(obj)

toRaw拿到Proxy对象对应的真实对象,如果存在直接返回

// 这种情况 const obj = { count: 0 } const state1 = reative(obj) const state2 = reative(state1)

有些情况无法被观测,则直接返回观测对象本身

const canObserve = (value: any): boolean => { return ( !value._isVue && !value._isVNode && observableValueRE.test(toTypeString(value)) && !nonReactiveValues.has(value) ) }

设置handlers,即get,set等属性访问器, 注意:collectionHandlers是用来处理观测对象为Set,Map等情况,很少见,这里就不讲了

const handlers = collectionTypes.has(target.constructor) ? collectionHandlers : baseHandlers

然后创建了Proxy对象,并把观测对象和Proxy对象,分别做映射

observed = new Proxy(target, handlers) toProxy.set(target, observed) toRaw.set(observed, target)

然后在targetMap做了target ==> Map的映射,这又是干嘛,注意:targetMap是全局的

export const targetMap: WeakMap<any, KeyToDepMap> = new WeakMap() if (!targetMap.has(target)) { targetMap.set(target, new Map()) }

在这里先给大家卖个关子,targetMap非常重要,是用来保存依赖的地方

讲完了reactive,可以回到一开始的引子

依赖收集

说到依赖收集,不得不提到,依赖的创建,那么Vue3.0是在哪里创建了渲染依赖呢,大家可以找到下面这段代码以及文件

// vue-next\packages\runtime-core\src\createRenderer.ts function setupRenderEffect( instance: ComponentInternalInstance, parentSuspense: HostSuspsenseBoundary | null, initialVNode: HostVNode, container: HostElement, anchor: HostNode | null, isSVG: boolean ) { // create reactive effect for rendering let mounted = false instance.update = effect(function componentEffect() { // ... }, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions) }

代码特别长,我剪掉了中间部分,大家还记得effect有个选项lazy吗,没错,它默认是false,也就会立即调用传入的componentEffect回调,在它内部调用了patch实现了组件的挂载。

敲黑板,关键来了,还记得effect调用,内部会调用run方法吗

function run(effect: ReactiveEffect, fn: Function, args: any[]): any { if (!effect.active) { return fn(...args) } if (activeReactiveEffectStack.indexOf(effect) === -1) { cleanup(effect) try { activeReactiveEffectStack.push(effect) return fn(...args) } finally { activeReactiveEffectStack.pop() } } }

这里进行了第一步的依赖收集,保存在全局数组中,为了方便触发get的对象,将依赖收集到自己的deps中
然后就是调用patch,进行组件挂载

if (!mounted) { const subTree = (instance.subTree = renderComponentRoot(instance)) // beforeMount hook if (instance.bm !== null) { invokeHooks(instance.bm) } patch(null, subTree, container, anchor, instance, parentSuspense, isSVG) initialVNode.el = subTree.el // mounted hook if (instance.m !== null) { queuePostRenderEffect(instance.m, parentSuspense) } mounted = true }

至于它内部实现,我就不讲了,不是本文重点,然后我们去编译的地方看看

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/23dafa4f1d3ac97518ded955668ba551.html