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

ref

对了,大家一定要先知道怎么用哦~

引子

先来段代码,大家可以直接复制哦,注意引用的文件

<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <script src="https://www.jb51.net/packages/vue/dist/vue.global.js"></script> </head> <body> <div></div> <script> const { reactive, computed, effect, watch, createApp } = Vue const App = { template: ` <div> <button @click="increment">{{ state.count }}</button> </div> `, setup() { const state = reactive({ count: 0 }) function increment(e) { state.count++ } effect(() => { console.log('count改变', state.count); }) return { state, increment } } } createApp().mount(App, '#app') </script> </body> </html>

这段代码,想必大家都看得懂,点击后count增加,视图也随之更新,effect监听了count改变,那么为什么effect能观察到count变化呢,还有为什么reactive可以实现响应式?

effect

为什么要先说这个函数呢,因为它和其他函数都息息相关,只有先了解它才能更好的理解其他响应式API

上源码

export function effect( fn: Function, options: ReactiveEffectOptions = EMPTY_OBJ ): ReactiveEffect { if ((fn as ReactiveEffect).isEffect) { fn = (fn as ReactiveEffect).raw } const effect = createReactiveEffect(fn, options) if (!options.lazy) { effect() } return effect }

if判断,判断如果传入的fn函数,它已经是effect了,也就是一个标识,直接获取该函数上的raw属性,这个属性后面会讲到

调用createReactiveEffect

如果options中有lazy,就会立即调用effect,其实本质上调用的还是传入的fn函数

// 了解一下options有哪些 { lazy?: boolean // 是否立即调用fn computed?: boolean // 是否是computed scheduler?: (run: Function) => void // 在调用fn之前执行 onTrack?: (event: DebuggerEvent) => void // 在依赖收集完成之后调用 onTrigger?: (event: DebuggerEvent) => void // 在调用fn之前执行,源码上来看和scheduler调用时机一样,只是传入参数不同 onStop?: () => void // 清除依赖完成后调用 }

返回effect

createReactiveEffect

上面提到了createReactiveEffect函数,我们来看看它的实现

function createReactiveEffect( fn: Function, options: ReactiveEffectOptions ): ReactiveEffect { // 又包装了一层函数 const effect = function effect(...args): any { return run(effect as ReactiveEffect, fn, args) } as ReactiveEffect effect.isEffect = true // 标识effect effect.active = true // 如果active effect.raw = fn // 传入的回调 effect.scheduler = options.scheduler effect.onTrack = options.onTrack effect.onTrigger = options.onTrigger effect.onStop = options.onStop effect.computed = options.computed effect.deps = [] // 用于收集依赖 return 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() } } }

他把依赖存储在了一个全局的数组中activeReactiveEffectStack, 他以栈的形式存储,调用完依赖后,会弹出,大家要留意一下这里,后面会用到

怎么样,是不是很简单~

reactive

export function reactive(target: object) { // 如果target是已经被readonly对象,那么直接返回对应的proxy对象 if (readonlyToRaw.has(target)) { return target } // 如果target是已经被readonly对象,那么直接返回对应的真实对象 if (readonlyValues.has(target)) { return readonly(target) } return createReactiveObject( target, rawToReactive, reactiveToRaw, mutableHandlers, mutableCollectionHandlers ) }

前两个if是用来处理这种情况的

// 情况一 const state1 = readonly({ count: 0 }) const state2 = reactive(state1) // 情况二 const obj = { count: 0 } const state1 = readonly(obj) const state2 = reactive(obj) 可以看到reactive它的参数是被readonly的对象,reactive不会对它再次创建响应式,而是通过Map映射,拿到对应的对象,即Proxy <==> Object的相互转换。 createReactiveObject创建响应式对象,注意它的参数 createReactiveObject( target, rawToReactive, // Object ==> Proxy reactiveToRaw, // Proxy ==> Object mutableHandlers, // get set has ... mutableCollectionHandlers // 很少会用,不讲了~ )

以上就是reative一开始所做的一些事情,下面继续分析createReactiveObject

createReactiveObject

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

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