?前兩篇寫了響應式系統(tǒng)的兩個核心模塊effect,reactivity宏侍,這篇寫一下響應式系統(tǒng)在源碼中的應用吧潮太。
Computed API
?話不多說,首先來看一下利用響應式系統(tǒng)實現computed api谆构,直接上代碼注釋裸扶。
// 3.x的computed 支持設置setter,setter的邏輯很簡單搬素,就不多說了呵晨,這里刪掉了相關的代碼。
// 大部分的情況下傳入的是一個function, 也就是getter
export function computed<T>(
getterOrOptions: ComputedGetter<T> | WritableComputedOptions<T>
) {
let getter: ComputedGetter<T>
let dirty = true
let value: T
let computed: ComputedRef<T>
// 每個computed中都會維護一個lazy effect實例
const runner = effect(getter, {
lazy: true,
// mark effect as computed so that it gets priority during trigger
computed: true,
// 注意:這個scheduler并不執(zhí)行effect熬尺,僅僅把dirty設置為true
scheduler: () => {
if (!dirty) {
dirty = true
// 觸發(fā)依賴這個computed的effects
trigger(computed, TriggerOpTypes.SET, 'value')
}
}
})
computed = {
_isRef: true,
// expose effect so computed can be stopped
effect: runner,
get value() {
// 如果數據是臟的摸屠,就執(zhí)行自身的effect獲取最新數據,否則直接返回緩存的數據
if (dirty) {
value = runner()
dirty = false
}
// 此時如果有active effect的話粱哼,就會被收集到computed的depsMap里面
track(computed, TrackOpTypes.GET, 'value')
return value
}
} as any
return computed
}
我試著用語言描述一下:
- computed和ref一樣季二,返回一個封裝的對象,實際上他的_isRef就是true
- 內部維護一個lazy effect,之前也講過胯舷,lazy effect不會立刻執(zhí)行
- 在第一次獲取這個computed的值的時候刻蚯,執(zhí)行這個lazy effect,此時active effect就是這個lazy effect桑嘶,computed里面的響應式數據都會收集到這個lazy effect炊汹,
- 當computed的響應式數據發(fā)生改變,由于設置了scheduler不翩,并不會去執(zhí)行這個lazy effect兵扬,而是把這個computed標示為臟,然后直接trigger這個computed下所有的effect口蝠。(如果computed是在render中調用的話器钟,那么就相當于重新去執(zhí)行render的過程)
Watch API
? 去看watch api的源碼packages/runtime-core/src/apiWatch.ts
, 會發(fā)現好長,其實watch api的原理最簡單了妙蔗,effect方法的作用是監(jiān)聽傳入的函數傲霸,如果響應式數據發(fā)生改變,那么就重新執(zhí)行這個函數眉反,而effect方法支持scheduler配置昙啄,如果傳入了scheduler方法,就不會直接重新執(zhí)行這個effect,我們只需要在shceduler方法里面去執(zhí)行watch api的回調即可寸五。
?終于寫完響應式原理相關的了梳凛,發(fā)現寫這種源碼解讀的文章是真難,不過這個過程需要組織語言去描述梳杏,自己還是加深了理解韧拒。后面接下去本來打算寫compile相關的內容,但是感覺如果像響應式這樣寫的話也太難面面俱到了十性,也沒有那么多精力叛溢。因此打算最后寫幾篇,內容就是vue3.x相比2.x快在哪里劲适,做了哪些優(yōu)化楷掉。