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

//vue-next\packages\runtime-core\src\component.ts function finishComponentSetup( instance: ComponentInternalInstance, parentSuspense: SuspenseBoundary | null ) { const Component = instance.type as ComponentOptions if (!instance.render) { if (Component.template && !Component.render) { if (compile) { Component.render = compile(Component.template, { onError(err) {} }) } else if (__DEV__) { warn( `Component provides template but the build of Vue you are running ` + `does not support on-the-fly template compilation. Either use the ` + `full build or pre-compile the template using Vue CLI.` ) } } if (__DEV__ && !Component.render) { warn( `Component is missing render function. Either provide a template or ` + `return a render function from setup().` ) } instance.render = (Component.render || NOOP) as RenderFunction } // ...其他 }

上面的代码是编译部分,我们来看看例子中编译后是什么样

(function anonymous( ) { const _Vue = Vue const _createVNode = Vue.createVNode const _hoisted_1 = { id: "box" } return function render() { with (this) { const { toString: _toString, createVNode: _createVNode, openBlock: _openBlock, createBlock: _createBlock } = _Vue return (_openBlock(), _createBlock("div", _hoisted_1, [ _createVNode("button", { onClick: increment }, _toString(state.count), 9 /* TEXT, PROPS */, ["onClick"]) ])) } } })

可以看到,编译的代码中,有使用到state.count,那么就会触发get访问器,从而收集依赖,至于为什么能直接访问到属性,原因是由于with设置了上下文,下面我们具体分析get

get

// vue-next\packages\reactivity\src\baseHandlers.ts function createGetter(isReadonly: boolean) { return function get(target: any, key: string | symbol, receiver: any) { const res = Reflect.get(target, key, receiver) if (typeof key === 'symbol' && builtInSymbols.has(key)) { return res } // _isRef if (isRef(res)) { return res.value } track(target, OperationTypes.GET, key) // 如果该属性对应的值还是对象,就继续递归创建响应式 return isObject(res) ? isReadonly ? // need to lazy access readonly and reactive here to avoid // circular dependency readonly(res) : reactive(res) : res } }

调用Reflect.get获取属性值

如果key是symbol并且是Symbol的一个属性,就直接返回该值

// 这种情况 const key = Symbol('key') const state = reative({ [key]: 'symbol value' }) state[key]

如果值为Ref返回该值的value,看到这里如果大家有了解过ref api的话就知道了,由于ref它自己实现了自己的get,set,所以不再需要执行后面的逻辑,这个在后面会讲

调用track

递归深度观测,使整个对象都为响应式

下面我会详细讲解

track

在讲它之前,先了解它有哪些参数

target: any, // 目标对象 type: OperationTypes, // 追踪数据变化类型,这里是get key?: string | symbol // 需要获取的key export const enum OperationTypes { SET = 'set', ADD = 'add', DELETE = 'delete', CLEAR = 'clear', GET = 'get', HAS = 'has', ITERATE = 'iterate' }

export function track( target: any, type: OperationTypes, key?: string | symbol ) { if (!shouldTrack) { return } // 获取activeReactiveEffectStack中的依赖 const effect = activeReactiveEffectStack[activeReactiveEffectStack.length - 1] if (effect) { if (type === OperationTypes.ITERATE) { key = ITERATE_KEY } // 获取目标对象对应的依赖map let depsMap = targetMap.get(target) if (depsMap === void 0) { targetMap.set(target, (depsMap = new Map())) } // 获取对应属性的依赖 let dep = depsMap.get(key as string | symbol) // 如果该依赖不存在 if (!dep) { // 设置属性对应依赖 depsMap.set(key as string | symbol, (dep = new Set())) } // 如果属性对应依赖set中不存在该依赖 if (!dep.has(effect)) { // 添加到依赖set中 dep.add(effect) effect.deps.push(dep) if (__DEV__ && effect.onTrack) { // 调用onTrack钩子 effect.onTrack({ effect, target, type, key }) } } } }

activeReactiveEffectStack我两次提到,从它这里拿到了依赖,注意后面执行完依赖后,会从它里面弹出

如果effect存在

从targetMap中获取对象,对饮的Map,具体的数据结构类似这样

const state = reative({ count: 0 }) effect(() => { console.log(state.count) }) // 依赖大致结构(随便写的,不太规范) { target(state):Map { count: Set (componentEffect渲染依赖, user自己添加的依赖) } }

如果该对象不存在Map,就初始化一个

如果该Map中属性对应的Set不存在,就初始化一个Set

添加依赖到Set中

添加依赖到effect自身的deps数组中

最后调用onTrack回调

// 调用onTrack钩子 effect.onTrack({ effect, target, type, key })

OK,Track实现大体就这样,是不是也很简单,有了这些基础,后面要讲的一些API就很容易理解了

set

当我们点击按钮后,就会触发set属性访问器

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

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