版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)
文檔簡介
第React深入分析useEffect源碼目錄熱身準(zhǔn)備初始化mount更新updateupdateEffect執(zhí)行副作用總結(jié)
熱身準(zhǔn)備
這里不再講useLayoutEffect,它和useEffect的代碼是一樣的,區(qū)別主要是:
執(zhí)行時(shí)機(jī)不同;useEffect是異步,useLayoutEffect是同步,會阻塞渲染;
初始化mount
mountEffect
在所有hook初始化時(shí)都會通過下面這行代碼實(shí)現(xiàn)hook結(jié)構(gòu)的初始化和存儲,這里不再講mountWorkInProgressHook方法
varhook=mountWorkInProgressHook();
在mountEffect方法中,只有這幾行代碼。先來解讀下幾個(gè)參數(shù):
fiberFlags:有副作用的更新標(biāo)記,用來標(biāo)記hook所在的fiber;hookFlags:副作用標(biāo)記;create:使用者傳入的回調(diào)函數(shù);deps:使用者傳入的數(shù)組依賴;
functionmountEffectImpl(fiberFlags,hookFlags,create,deps){
//hook初始化
varhook=mountWorkInProgressHook();
//判斷是否有傳入deps,如果有會作為下次更新的deps
varnextDeps=deps===undefinednull:deps;
//給hook所在的fiber打上有副作用的更新的標(biāo)記
currentlyRenderingFiber$1.flags|=fiberFlags;
//將副作用操作存放到fiber.memoizedState.hook.memoizedState中
hook.memoizedState=pushEffect(HasEffect|hookFlags,create,undefined,nextDeps);
上面代碼中都有注釋,接下來我們看看React是如何存放副作用更新操作的,主要就是pushEffect方法
functionpushEffect(tag,create,destroy,deps){
//初始化副作用結(jié)構(gòu),
vareffect={
tag:tag,
create:create,//回調(diào)函數(shù)
destroy:destroy,//回調(diào)函數(shù)里的return(mount時(shí)是undefined)
deps:deps,//依賴數(shù)組
//閉環(huán)鏈表
next:null
//下面的一大段代碼看著復(fù)雜,但是有沒有很熟悉的感覺?
varcomponentUpdateQueue=currentlyRenderingFiber$1.updateQueue;
if(componentUpdateQueue===null){
componentUpdateQueue=createFunctionComponentUpdateQueue();
currentlyRenderingFiber$1.updateQueue=componentUpdateQueue;
//effect.next=effect形成環(huán)形鏈表
componentUpdateQueue.lastEffect=effect.next=effect;
}else{
varlastEffect=componentUpdateQueue.lastEffect;
if(lastEffect===null){
componentUpdateQueue.lastEffect=effect.next=effect;
}else{
varfirstEffect=lastEffect.next;
lastEffect.next=effect;
effect.next=firstEffect;
componentUpdateQueue.lastEffect=effect;
returneffect;
}
上面這段代碼除了初始化副作用的結(jié)構(gòu)代碼外,都是我們前面講過的操作閉環(huán)鏈表,向鏈表末尾添加新的effect,該effect.next指向fisrtEffect,并且鏈表當(dāng)前的指針指向最新添加的effect。
useEffect的初始化就這么簡單,簡單總結(jié)一下:給hook所在的fiber打上副作用更新標(biāo)記,并且fiber.memoizedState.hook.memoizedState和fiber.updateQueue存儲了相關(guān)的副作用,這些副作用通過閉環(huán)鏈表的結(jié)構(gòu)存儲。
相關(guān)參考視頻講解:傳送門
更新update
updateEffect
updateWorkInProgressHook在上篇文章也已講過,不再詳述,主要功能就是創(chuàng)建一個(gè)帶有回調(diào)函數(shù)的newHook去覆蓋之前的hook。
functionupdateEffectImpl(fiberFlags,hookFlags,create,deps){
varhook=updateWorkInProgressHook();
varnextDeps=deps===undefinednull:deps;
vardestroy=undefined;
if(currentHook!==null){
varprevEffect=currentHook.memoizedState;
destroy=prevEffect.destroy;
if(nextDeps!==null){
varprevDeps=prevEffect.deps;
//比較兩次依賴數(shù)組中的值是否有變化
if(areHookInputsEqual(nextDeps,prevDeps)){
//和之前初始化時(shí)一樣
pushEffect(hookFlags,create,destroy,nextDeps);
return;
//和之前初始化時(shí)一樣
currentlyRenderingFiber$1.flags|=fiberFlags;
hook.memoizedState=pushEffect(HasEffect|hookFlags,create,destroy,nextDeps);
}
相信眼眼尖的看官已經(jīng)注意到上面代碼中有兩個(gè)pushEffect,一個(gè)沒有賦值給hook.memoizedState,一個(gè)賦值了,這兩者有什么區(qū)別呢?
先保留著這個(gè)疑問,先來了解下下面這行代碼都做了些什么,因?yàn)樗炀土藘蓚€(gè)pushEffect。
if(areHookInputsEqual(nextDeps,prevDeps)){...}
functionareHookInputsEqual(nextDeps,prevDeps){
//沒有傳deps的情況返回false
if(prevDeps===null){
returnfalse;
//deps不是[],且其中的值有變動才會返回false
for(vari=0;iprevDeps.lengthinextDeps.length;i++){
if(objectIs(nextDeps[i],prevDeps[i])){
continue;
returnfalse;
//deps=[],或者deps里面的值沒有變化會返回true
returntrue;
}
它會判斷兩次依賴數(shù)組中的值是否有變化以及deps是否是空數(shù)組來決定返回true和false,返回true表明這次不需要調(diào)用回調(diào)函數(shù)。
現(xiàn)在我們明白了兩次pushEffect的異同,if內(nèi)部的pushEffect是不需要調(diào)用的回調(diào)函數(shù),外面的pushEffect是需要調(diào)用的。再來仔細(xì)看下這兩行代碼:
//if內(nèi)部的,第一個(gè)參數(shù)是hookFlags=4
pushEffect(hookFlags,create,destroy,nextDeps);
//if外部的,第一個(gè)參數(shù)是HasEffect|hookFlags=5
hook.memoizedState=pushEffect(HasEffect|hookFlags,create,destroy,nextDeps);
這兩行代碼的區(qū)別是傳入的第一個(gè)參數(shù)不同,而第一個(gè)參數(shù)就是effect.tag的值,effect.tag=4不會添加到副作用執(zhí)行隊(duì)列,而effect.tag=5可以。沒有添加到副作用執(zhí)行隊(duì)列的effect就不會執(zhí)行。這樣就巧妙的實(shí)現(xiàn)了useEffect基于deps來判斷是否需要執(zhí)行回調(diào)函數(shù)。
到這里,我們搞明白了,不管useEffect里的deps有沒有變化都會為回調(diào)函數(shù)創(chuàng)建effect并添加到effect鏈表和fiber.updateQueue中,但是React會根據(jù)effect.tag來決定該effect是否要添加到副作用執(zhí)行隊(duì)列中去執(zhí)行。
執(zhí)行副作用
我們現(xiàn)在知道了,useEffect是異步執(zhí)行的。那么這個(gè)回調(diào)函數(shù)副作用會在什么時(shí)候執(zhí)行呢?useEffect回調(diào)函數(shù)會在layout階段之后執(zhí)行?,F(xiàn)在我們來了解下具體調(diào)用執(zhí)行的流程。
我畫了一個(gè)簡單的流程圖,大致描述了下調(diào)用流程。首先在mutation之前階段,基于副作用創(chuàng)建任務(wù)并放到taskQueue中,同時(shí)會執(zhí)行requestHostCallback,這個(gè)方法就涉及到了異步了,它首先考慮使用MessageChannel實(shí)現(xiàn)異步,其次會考慮使用setTimeout實(shí)現(xiàn)。使用MessageChannel時(shí),requestHostCallback會馬上執(zhí)行port.postMessage(null);,這樣就可以在異步的第一時(shí)間執(zhí)行workLoop,workLoop會遍歷taskQueue,執(zhí)行任務(wù),如果是useEffect的effect任務(wù),會調(diào)用flusnPassiveEffects。
Q:可能有人會疑惑為什么優(yōu)先考慮MessageChannel?
A:首先我們要明白React調(diào)度更新的目的是為了時(shí)間分片,意思是每隔一段時(shí)間就把主線程還給瀏覽器,避免長時(shí)間占用主線程導(dǎo)致頁面卡頓。使用MessageChannel和SetTimeout的目的都是為了創(chuàng)建宏任務(wù),因?yàn)楹耆蝿?wù)會在當(dāng)前微任務(wù)都執(zhí)行完后,等到瀏覽器主線程空閑后才會執(zhí)行。不優(yōu)先考慮setTimeout的原因是,setTimeout執(zhí)行時(shí)間不準(zhǔn)確,會造成時(shí)間浪費(fèi),即使是setTimeout(fn,0),感興趣的可以去自己了解下,本文不做贅述了。
在schedulePassiveEffects中,會決定是否執(zhí)行effect鏈表中的effect,判斷的依據(jù)就是每個(gè)effect上的effect.tag:
functionschedulePassiveEffects(finishedWork){
varupdateQueue=finishedWork.updateQueue;
varlastEffect=updateQueue!==nullupdateQueue.lastEffect:null;
if(lastEffect!==null){
varfirstEffect=lastEffect.next;
vareffect=firstEffect;
//遍歷effect鏈表
do{
var_effect=effect,
next=_effect.next,
tag=_effect.tag;
//基于effect.tag決定是否添加到副作用執(zhí)行隊(duì)列
if((tagPassive$1)!==NoFlags$1(tagHasEffect)!==NoFlags$1){
enqueuePendingPassiveHookEffectUnmount(finishedWork,effect);
enqueuePendingPassiveHookEffectMount(finishedWork,effect);
effect=next;
}while(effect!==firstEffect);
}
在flushPassiveEffects中,會先執(zhí)行上次更新動作的銷毀函數(shù),然后再執(zhí)行本次更新動作的回調(diào)函數(shù),并且會把回調(diào)函數(shù)的return作為下次更新動作的銷毀函數(shù)。
functionflushPassiveEffectsImpl(){
//執(zhí)行上次更新動作的銷毀函數(shù)
varunmountEffects=pendingPassiveHookEffectsUnmou
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 衛(wèi)生用品更衣室管理制度
- 衛(wèi)生院行風(fēng)督查制度
- 衛(wèi)生院三病物資管理制度
- 生活區(qū)衛(wèi)生物品管理制度
- 衛(wèi)生院疾病預(yù)防管理制度
- 衛(wèi)生所規(guī)范管理制度
- 養(yǎng)殖場日常衛(wèi)生管理制度
- 幼兒園8項(xiàng)衛(wèi)生管理制度
- 衛(wèi)生所首診負(fù)責(zé)制度
- 衛(wèi)生院新冠病人轉(zhuǎn)診制度
- 九年級年級組長工作總結(jié)
- 2025屆安徽省省級示范高中高一物理第一學(xué)期期末經(jīng)典試題含解析
- 現(xiàn)金日記賬模板(出納版)
- DB34T 1948-2013 建設(shè)工程造價(jià)咨詢檔案立卷標(biāo)準(zhǔn)
- 2024中藥藥渣處理協(xié)議
- 心源性暈厥的查房
- 機(jī)械氣道廓清技術(shù)臨床應(yīng)用專家共識(2023版)解讀
- 壓力性損傷風(fēng)險(xiǎn)評估與管理護(hù)理課件
- 專家解析:渲染,烘托等的區(qū)別課件
- 廣州花城匯UUPARK招商手冊
- 20S517 排水管道出水口
評論
0/150
提交評論