研究 React 應(yīng)用程序的內(nèi)部運(yùn)行機(jī)制要找到切入點(diǎn)
前言
做為一名 React 開發(fā)者,你是否思考過為什么我們?cè)陂_發(fā) React 程序時(shí)并未直接(使用 JavaScript API)操作 DOM 卻能更新 DOM,以及當(dāng)組件中的 this.setState( ... )
操作被觸發(fā)后 React 內(nèi)部到底做了哪些工作?本節(jié)將會(huì)介紹研究 React 內(nèi)部運(yùn)行機(jī)制的切入點(diǎn)以及整體研究思路。
如何研究 React 內(nèi)部運(yùn)行機(jī)制
我們想要知道 React 的內(nèi)部運(yùn)行機(jī)制,實(shí)際上就是要探索 React 如何將組件映射屏幕,以及組件中的 state 和 prop 發(fā)生了變化之后 React 如何將這些「變化」更新到屏幕。
想要搞清楚上面的兩個(gè)問題,我們需要對(duì) React 應(yīng)用程序的首次渲染過程和更新渲染過程有整體的認(rèn)知。那么,應(yīng)用程序的首次渲染和更新渲染兩個(gè)過程中 React 內(nèi)部要做的工作有什么相同點(diǎn)與不同點(diǎn)呢?
圖 1.1.1 React 應(yīng)用程序的兩種渲染
應(yīng)用程序首次渲染時(shí) React 會(huì)做一些基建工作,比如 將組件轉(zhuǎn)化為元素,構(gòu)建更新隊(duì)列,構(gòu)建 workInProgress 對(duì)象樹以及構(gòu)建 Effect list(副作用列表) 等。這些基建工作是構(gòu)建 React Fiber 架構(gòu)的關(guān)鍵環(huán)節(jié)。而在應(yīng)用程序更新渲染時(shí),React Fiber 架構(gòu)的實(shí)體 —fiberRoot 對(duì)象已經(jīng)存在于內(nèi)存中,此時(shí),React 更加關(guān)心的是 計(jì)算出 Fiber 架構(gòu)中各個(gè)結(jié)點(diǎn)的前后變化,并將【變化部分】更新到屏幕。
現(xiàn)在,我們對(duì)上面圖中的兩個(gè)渲染過程中的關(guān)鍵環(huán)節(jié)用流程圖表示,見圖 1.1.2。
圖 1.1.2 React 應(yīng)用程序的兩種渲染流程
React 應(yīng)用程序首次渲染時(shí)的關(guān)鍵環(huán)節(jié)解析
- 構(gòu)建 fiberRoot 對(duì)象(
FiberRootNode
構(gòu)造函數(shù)的實(shí)例),fiberRoot 對(duì)象是整個(gè) Fiber 架構(gòu)的根結(jié)點(diǎn)對(duì)象。 - 將更新加入到更新隊(duì)列,此時(shí)的更新內(nèi)容為應(yīng)用程序的根組件。
- 應(yīng)用程序進(jìn)入 render 階段,在該階段 React 的主要工作是構(gòu)建 workInProgress 樹(一顆 Fiber 樹)。
- 構(gòu)建 workInProgress 樹的過程中會(huì)做一些重要的工作,如為結(jié)點(diǎn)標(biāo)記 effectTag,對(duì)結(jié)點(diǎn)進(jìn)行 diff 處理,收集 Effect List(副作用列表),調(diào)用生命周期函數(shù)等。
- 當(dāng)收集好 Effect List 后則進(jìn)入 commit 階段,在該階段 React 主要工作就是將 Effect List 更新到屏幕,然后渲染結(jié)束。
React 應(yīng)用程序更新渲染時(shí)的關(guān)鍵環(huán)節(jié)解析
- 相對(duì)于應(yīng)用程序的首次渲染,更新渲染流程的主要區(qū)別有,不再重新構(gòu)建 fiberRoot 對(duì)象,因?yàn)樵搶?duì)象已經(jīng)存在于內(nèi)存中。
- 此時(shí)的更新內(nèi)容一般為組件內(nèi)部發(fā)生變化的
state
和props
。 - 在進(jìn)入 render 階段前要進(jìn)行任務(wù)調(diào)度,更新任務(wù)需要申請(qǐng)(檢查是否有)執(zhí)行權(quán),得到更新執(zhí)行權(quán)的任務(wù)才能進(jìn)行后續(xù)渲染工作。
- 此時(shí)構(gòu)建 workInProgress 樹時(shí)也會(huì)盡可能的復(fù)用上一次創(chuàng)建的 Fiber 結(jié)點(diǎn),同時(shí)對(duì)需要更新的結(jié)點(diǎn)標(biāo)記對(duì)應(yīng)的 effectTag。
- 在 commit 階段得到的 Effect List 是被標(biāo)記了 effectTag 的 Fiber 結(jié)點(diǎn)集合(一個(gè)鏈表),其一般是 workInProgress 樹的子集。
注:上面提到的 React 在渲染過程中的工作多是抽象內(nèi)容,后面我們會(huì)對(duì)其中每一項(xiàng)工作通過獲取真實(shí)運(yùn)行狀態(tài)進(jìn)行詳細(xì)分析。
研究 React 內(nèi)部運(yùn)行機(jī)制 — 思維導(dǎo)圖
圖 1.1.3 專欄學(xué)習(xí)思維導(dǎo)圖
小結(jié)
本文提到的有關(guān) React Fiber 架構(gòu)的相關(guān)內(nèi)容以及構(gòu)建 workInProgress 樹、Effect List 等內(nèi)容在后面章節(jié)中會(huì)有詳細(xì)介紹?,F(xiàn)在我們知道,要想打開 React 應(yīng)用程序內(nèi)部運(yùn)行機(jī)制的大門,解鎖 ReactDOM.render
函數(shù)是關(guān)鍵,因?yàn)?ReactDOM.render
函數(shù)是 React 應(yīng)用程序首次渲染時(shí)的入口函數(shù),我們需要對(duì)它有更多的了解,ReactDOM.render
函數(shù)的更多介紹見本章第二節(jié)。