Vue3實(shí)現(xiàn)批量異步更新
寫(xiě)在前面
這是Vue3源碼分析的第三篇,與響應(yīng)式系統(tǒng)中調(diào)度執(zhí)行有關(guān),其中computed、watch等核心功能都離不開(kāi)它,可見(jiàn)其重要程度。
除了實(shí)現(xiàn)可調(diào)度性,我們還會(huì)借助它來(lái)實(shí)現(xiàn)vue中一個(gè)非常重要的功能,批量更新或者叫異步更新
多次修改數(shù)據(jù)(例如自身num10次),只進(jìn)行一次頁(yè)面渲染(頁(yè)面只會(huì)渲染最后一次num10)。
- 面試官:Vue3響應(yīng)式系統(tǒng)都不會(huì)寫(xiě),還敢說(shuō)精通?
- 面試官:你覺(jué)得Vue的響應(yīng)式系統(tǒng)僅僅是一個(gè)Proxy?
什么是調(diào)度執(zhí)行
什么是調(diào)度執(zhí)行?
指的是響應(yīng)式數(shù)據(jù)發(fā)生變化出發(fā)副作用函數(shù)重新執(zhí)行時(shí),我們有能力去決定副作用函數(shù)的執(zhí)行時(shí)機(jī)、次數(shù)和方式。
來(lái)看個(gè)例子
const?state?=?reactive({ ??num:?1 }) effect(()?=>?{ ??console.log('num',?state.num) }) state.num++ console.log('end')
如果我們想要它按照這個(gè)順序書(shū)序呢?
1
end
2
你可能會(huì)說(shuō),我調(diào)換一下代碼順序就好了哇?。?!
const?state?=?reactive({ ??num:?1 }) effect(()?=>?{ ??console.log('num',?state.num) }) console.log('end') state.num++
瞬間就解決了問(wèn)題。不過(guò)看起來(lái)這不是我們想要最終答案。
我們想要通過(guò)實(shí)現(xiàn)可調(diào)度性來(lái)解決這個(gè)問(wèn)題。
如何實(shí)現(xiàn)可調(diào)度
我們從結(jié)果出發(fā)來(lái)思考如何實(shí)現(xiàn)可調(diào)度的特性。
const?state?=?reactive({ ??num:?1 }) effect(()?=>?{ ??console.log(state.num) },?{ ??//?注意這里,假如num發(fā)生變化的時(shí)候執(zhí)行的是scheduler函數(shù) ??//?那么end將會(huì)被先執(zhí)行,因?yàn)槲覀冇胹etTimeout包裹了一層fn ??scheduler?(fn)?{ ????//?異步執(zhí)行 ????setTimeout(()?=>?{ ??????fn() ????},?0) ??} }) state.num++ console.log('end')
看到這里也許你已經(jīng)明白了,我們將通過(guò)scheduler來(lái)自主控制副作用函數(shù)的執(zhí)行時(shí)機(jī)。
在這之前,執(zhí)行state.num++
之后,console.log(state.num)
將會(huì)被馬上執(zhí)行,而添加scheduler后,num發(fā)生變化后將執(zhí)行scheduler中的邏輯。
源碼實(shí)現(xiàn)
雖然可調(diào)度性在Vue中非常重要,但實(shí)現(xiàn)這個(gè)機(jī)制卻非常簡(jiǎn)單,我們甚至只要增加兩行代碼就可以搞定。
第一行代碼
//?增加options參數(shù) const?effect?=?function?(fn,?options?=?{})?{ ??const?effectFn?=?()?=>?{ ???//?.... ??} ??//?... ??//?將options參數(shù)掛在effectFn上,便于effectFn執(zhí)行時(shí)可以讀取到scheduler ??effectFn.options?=?options }
第二行代碼
function?trigger(target,?key)?{ //?... ??effectsToRun.forEach((effectFn)?=>?{ ????//?當(dāng)指定了scheduler時(shí),將執(zhí)行scheduler而不是注冊(cè)的副作用函數(shù)effectFn ????if?(effectFn.options.scheduler)?{ ??????effectFn.options.scheduler(effectFn) ????}?else?{ ??????effectFn() ????} ??}) }
是不是簡(jiǎn)單到離譜?
批量更新 & 異步更新
來(lái)看段詭異的代碼,請(qǐng)問(wèn)num會(huì)被執(zhí)行多少次?100還是101?
const?state?=?reactive({ ??num:?1 }) effect(()?=>?{ ??console.log('num',?state.num) }) let?count?=?100 while?(count--)?{ ??state.num++ }
對(duì)于頁(yè)面渲染來(lái)說(shuō)1到101中間的2~100僅僅只是過(guò)程,并不是最終的結(jié)果,處于性能考慮Vue只會(huì)渲染最后一次的101。
Vue是如何做到的呢?
利用可調(diào)度性,再加點(diǎn)事件循環(huán)的知識(shí),我們就可以做到這件事。
- num的每次變化都會(huì)導(dǎo)致scheduler的執(zhí)行,并將注冊(cè)好的副作用函數(shù)存入jobQueue隊(duì)列,因?yàn)镾et本身的去重性質(zhì),最終只會(huì)存在一個(gè)fn
- 利用Promise微任務(wù)的特性,當(dāng)num被更改100次之后同步代碼全部執(zhí)行結(jié)束后,then回調(diào)將會(huì)被執(zhí)行,此時(shí)num已經(jīng)是101,而jobQueue中也只有一個(gè)fn,所以最終只會(huì)打印一次101
?const?state?=?reactive({ ??num:?1 }) const?jobQueue?=?new?Set() const?p?=?Promise.resolve() let?isFlushing?=?false const?flushJob?=?()?=>?{ ??if?(isFlushing)?{ ????return ??} ??isFlushing?=?true ??//?微任務(wù) ??p.then(()?=>?{ ????jobQueue.forEach((job)?=>?job()) ??}).finally(()?=>?{ ????//?結(jié)束后充值設(shè)置為false ????isFlushing?=?false ??}) } effect(()?=>?{ ??console.log('num',?state.num) },?{ ??scheduler?(fn)?{ ????//?每次數(shù)據(jù)發(fā)生變化都往隊(duì)列中添加副作用函數(shù) ????jobQueue.add(fn) ????//?并嘗試刷新job,但是一個(gè)微任務(wù)只會(huì)在事件循環(huán)中執(zhí)行一次,所以哪怕num變化了100次,最后也只會(huì)執(zhí)行一次副作用函數(shù) ????flushJob() ??} }) let?count?=?100 while?(count--)?{ ??state.num++ }
到此這篇關(guān)于Vue3實(shí)現(xiàn)批量異步更新的文章就介紹到這了,更多相關(guān)Vue3異步更新內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何在Vue3中正確使用ElementPlus,親測(cè)有效,避坑
這篇文章主要介紹了如何在Vue3中正確使用ElementPlus,親測(cè)有效,避坑!具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03vue.js數(shù)據(jù)加載完成前顯示原代碼{{代碼}}問(wèn)題及解決
這篇文章主要介紹了vue.js數(shù)據(jù)加載完成前顯示原代碼{{代碼}}問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07記錄vue項(xiàng)目中遇到的一點(diǎn)小問(wèn)題
本文是腳本之家小編給大家收藏整理的關(guān)于vue項(xiàng)目中遇到的一點(diǎn)小問(wèn)題,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-05-05vue 項(xiàng)目 iOS WKWebView 加載
這篇文章主要介紹了vue 項(xiàng)目 iOS WKWebView 加載問(wèn)題,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04vue開(kāi)發(fā)利器之unplugin-auto-import的使用
unplugin-auto-import 解決了vue3-hook、vue-router、useVue等多個(gè)插件的自動(dòng)導(dǎo)入,也支持自定義插件的自動(dòng)導(dǎo)入,下面這篇文章主要給大家介紹了關(guān)于vue開(kāi)發(fā)利器之unplugin-auto-import使用的相關(guān)資料,需要的朋友可以參考下2023-02-02解決Vite打包后直接使用瀏覽器打開(kāi),顯示空白問(wèn)題
這篇文章主要介紹了解決Vite打包后直接使用瀏覽器打開(kāi),顯示空白問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03