React Fiber源碼深入分析
前言
本次React源碼參考版本為17.0.3
。
React架構(gòu)前世今生
查閱文檔了解到, React@16.x
是個分水嶺。
React@15及之前
在16之前,React架構(gòu)大致可以分為兩層:
- Reconciler: 主要職責是對比查找更新前后的變化的組件;
- Renderer: 主要職責是基于變化渲染頁面;
但是React團隊意識到這樣的架構(gòu)有致命問題: 因為在React15中,組件的更新是基于遞歸查找實現(xiàn)的,這樣一旦開始遞歸,是沒有辦法中斷的,如果組件層級很深,就會出現(xiàn)性能問題,導致頁面卡頓。
React@16及之后
為了解決這樣的問題,React團隊在React@16
進行了重構(gòu),引入了新的架構(gòu)模型:
- Reconciler: 主要職責是對比查找更新前后的變化的組件;
- Renderer: 主要職責是基于變化渲染頁面;
- Scheduler: 主要職責是區(qū)分任務(wù)優(yōu)先級,優(yōu)先執(zhí)行高優(yōu)先級的任務(wù);
新的架構(gòu)在原來的基礎(chǔ)上引入了Scheduler(調(diào)度器),這個東西是React團隊參考瀏覽器的API:requestIdleCallback
實現(xiàn)的。它的主要作用就是調(diào)度更新任務(wù):
- 一方面可以中斷當前任務(wù)執(zhí)行更高優(yōu)先級的任務(wù);
- 另一方面能判斷瀏覽器空閑時間,在恰當?shù)臅r間將主動權(quán)給到瀏覽器,保證頁面性能;并在瀏覽器下次空閑時繼續(xù)之前中斷的任務(wù); 這樣就將之前的不可中斷的同步更新變成了異步可中斷更新,不直接使用瀏覽器API可能考慮到兼容問題,可能也有別的方面的考量。
下面是新的React架構(gòu)更新模型:
這個新的架構(gòu)在進入Renderer之前的流程是可以被中斷的,主要有下列兩種情況:
- 進入了更高優(yōu)先級的任務(wù);
- 瀏覽器在當前幀沒有剩余空閑時間了;
Fiber
Fiber簡單的理解就是React15
版本的虛擬DOM。
Fiber簡單理解
如果將新的React架構(gòu)比作一個公司,F(xiàn)iber在新的架構(gòu)里承擔的就是這個公司的員工,員工也有等級,老板,部長,基層,每個人有自己的職責,知道自己在哪個節(jié)點該做什么工作,并將未完成的工作記住等第二天上班繼續(xù)完成,從而保證公司的順利運行。而每個Fiber對應(yīng)一個React element
:
假如有這樣一段代碼:
function App() { return ( <div> <span>牛牛</span> <span>不怕困難</span> </div> ) }
上面的代碼的抽象Fiber樹:
其中的每個方塊都是一個Fiber,它們通過child, return, sibling
連接對方構(gòu)成一個Fiber樹。相關(guān)參考視頻講解:傳送門
Fiber結(jié)構(gòu)
來看一個Fiber會有哪些屬性:
function FiberNode(tag, pendingProps, key, mode) { // Instance this.tag = tag; // 組件類型 this.key = key; // 組件props上的key this.elementType = null; // ReactElement.type 組件的dom類型, 比如`div, p` this.type = null; // 異步組件resolved之后返回的內(nèi)容 this.stateNode = null; // 在瀏覽器環(huán)境對應(yīng)dom節(jié)點 this.return = null; // 指向父節(jié)點 this.child = null; // 孩子節(jié)點 this.sibling = null; // 兄弟節(jié)點, 兄弟節(jié)點的return指向同一個父節(jié)點 this.index = 0; this.ref = null; // ref this.pendingProps = pendingProps; // 新的props this.memoizedProps = null; // 上一次渲染完成的props this.updateQueue = null; // 組件產(chǎn)生的update信息會放在這個隊列 this.memoizedState = null; // // 上一次渲染完成的state this.dependencies = null; this.mode = mode; // Effects this.flags = NoFlags; // 相當于之前的effectTag, 記錄side effect類型 this.nextEffect = null; // 單鏈表結(jié)構(gòu), 便于快速查找下一個side effect this.firstEffect = null; // fiber中第一個side effect this.lastEffect = null; // fiber中最后一個side effect this.lanes = NoLanes; // 優(yōu)先級相關(guān) this.childLanes = NoLanes; // 優(yōu)先級相關(guān) this.alternate = null; // 對應(yīng)的是current fiber }
Fiber工作原理
在弄明白Fiber工作原理之前,我們要先明確一個認知:新的React架構(gòu)使用了兩個Fiber樹。
- 一個Fiber樹是當前頁面dom的抽象,叫
current
; - 另一個Fiber樹是在內(nèi)存中執(zhí)行更新任務(wù)dom的抽象,叫
workInProgress
;
這樣做是為了方便比對變化組件,并降低創(chuàng)建的成本,盡可能復用現(xiàn)有代碼邏輯,從而提高渲染效率。
mount
React代碼在第一次執(zhí)行時,因為頁面還沒有渲染出來,此時是沒有current
樹的,只有一個正在構(gòu)建DOM的workInProgress
樹。
假如我們有這樣一段代碼:
function App() { return ( <div> <span>牛牛</span> <span>不怕困難</span> </div> ) } ReactDOM.render(<App/>, document.querySelector('#root'));
基于上面的代碼在mount
會生成這樣的Fiber樹:
可以看到這個圖只是在前面的圖上增加了fiberRoot
和rootFiber
兩個Fiber節(jié)點。
- fiberRoot:整個React應(yīng)用的根節(jié)點;
- rootFiber: 某個組件樹的根節(jié)點;(因為我們可能多次使用
React.render()
函數(shù),這樣就會有多個rootFiber)
圖中此時fiberRoot對應(yīng)的rootFiber下面還是空的,因為此時是第一次渲染,頁面上沒有任何東西,當workInProgress
樹構(gòu)建完成,在mutation
之后,layout
之前,fiberRootd的current
指針會指向workInProgress
樹,把它作為新的current
樹,此時結(jié)構(gòu)會變成這樣:
這時頁面渲染完成了,等待下次觸發(fā)更新時會從current
樹進行拷貝生成workInProgress
樹,然后比對更新。
update
如果我們在上面的代碼中觸發(fā)更新,將牛牛文本改成了勇敢牛牛,React代碼就會開始進行任務(wù)調(diào)度,因為只有這一個任務(wù),會馬上執(zhí)行,會從current
樹的rootFiber進行拷貝生成workInProgress
樹的根節(jié)點,在經(jīng)過向下遍歷比對,發(fā)現(xiàn)相同的就直接從current
樹上拷貝復用,直到比對到葉子節(jié)點的牛牛文本變了,這時才會生成新的Fiber(這里只是為了方便解釋,其實我這里使用的代碼牛牛不會生成新的Fiber,因為是純文本,只會替換父級節(jié)點的props)
到此這篇關(guān)于React Fiber源碼深入分析的文章就介紹到這了,更多相關(guān)React Fiber內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
如何不使用eject修改create-react-app的配置
許多剛開始接觸create-react-app框架的同學,不免都會有個疑問:如何在不執(zhí)行eject操作的同時,修改create-react-app的配置。2021-04-04從零開始最小實現(xiàn)react服務(wù)器渲染詳解
這篇文章主要介紹了從零開始最小實現(xiàn)react服務(wù)器渲染詳解,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-01-01ReactiveCocoa代碼實踐之-UI組件的RAC信號操作
這篇文章主要介紹了ReactiveCocoa代碼實踐之-UI組件的RAC信號操作 的相關(guān)資料,需要的朋友可以參考下2016-04-04