掃二維碼與項目經(jīng)理溝通
我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流

成都創(chuàng)新互聯(lián)是一家專注于做網(wǎng)站、網(wǎng)站制作與策劃設(shè)計,新泰網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)做網(wǎng)站,專注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:新泰等地區(qū)。新泰做網(wǎng)站價格咨詢:028-86922220
Reconciler 是 React 核心邏輯所在的模塊,中文名叫協(xié)調(diào)器。
在React中,Reconciler?(協(xié)調(diào)器)是負責(zé)管理虛擬DOM樹更新的關(guān)鍵部分。當組件狀態(tài)或?qū)傩园l(fā)生更改時,Reconciler的任務(wù)是確定如何有效地更新DOM來反映這些更改。這個過程通常被稱為 "協(xié)調(diào)"(Reconciliation)。
Reconciler?的核心思想是通過將新的虛擬DOM樹與舊的虛擬DOM樹進行比較,找出需要實際更新的部分,然后最小化實際DOM操作的數(shù)量。這個過程被稱為"diffing"算法。
在傳統(tǒng)的庫(jQuery)工作原理(過程驅(qū)動)。
在傳統(tǒng)的前端開發(fā)中使用的jQuery?庫的工作原理主要是通過一個簡化和統(tǒng)一的API,使得開發(fā)者能夠更容易地操作DOM、處理事件、創(chuàng)建動畫以及發(fā)起AJAX請求等。而不是描述UI的狀態(tài)。所以jQuery的工作原理是過程驅(qū)動的。
現(xiàn)代的前端框架結(jié)構(gòu)與工作原理(狀態(tài)驅(qū)動)。
現(xiàn)代的前端框架結(jié)構(gòu)與工作原理
現(xiàn)代框架都需要“編譯”這一步驟,用于:
“編譯”可以選擇兩個時機執(zhí)行:
大部分采用模板語法描述UI的前端框架都會進行AOT優(yōu)化,例如:Vue3、Angular、Svelte。
其本質(zhì)原因在于模板語法時固定的,固定意味著“可分析”,“可分析”意味著在編譯時可以標記模板語法中的靜態(tài)部分(不變的部分)與動態(tài)部分(包含自變量、可變的部分)。
但采用JSX語法描述UI的前端框架很難從 AOT中受益,因為JSX是ES的語法糖,ES語句的靈活性使其很難進行靜態(tài)分析。
拓展 那么Template語法是如何從中受益的呢?
模板語法由于在構(gòu)建時已經(jīng)被編譯成可執(zhí)行的JavaScript代碼,運行時無需再進行解析和編譯,從而減少了性能開銷。
回歸主題,根據(jù)前面的學(xué)習(xí),我們知道了 JSX 方法執(zhí)行后會返回一個新的 React 元素(ReactElement)。React 元素是一個輕量級的對象,描述了要渲染的 UI 組件的類型(type)、屬性(props),和子元素(children)等信息。
這里可以給自己個問題,如果ReactElement?作為reconciler核心模塊操作的數(shù)據(jù)結(jié)構(gòu),會存在哪些問題:
從下圖中可以看到,ReactELement這種數(shù)據(jù)結(jié)構(gòu)很有限,在節(jié)點屬性關(guān)聯(lián)方面也只有 children,并沒有保存兄弟節(jié)點以及父節(jié)點之間的關(guān)系:
當然在React 16版本之前,React 使用的是名為Stack Reconciler的舊調(diào)和算法。Stack Reconciler 的核心是遞歸遍歷組件樹,把數(shù)據(jù)保存在遞歸調(diào)用棧中。它使用的深層遞歸遍歷方法。
但是使用遞歸遍歷組件樹時,會導(dǎo)致一些問題:
為了解決這些問題,React 引入了 Fiber Reconciler?。Fiber Reconciler? 使用了一種名為 "Fiber" 的新數(shù)據(jù)結(jié)構(gòu)來表示組件樹。
它的特點:
FiberNode 是虛擬DOM在React中的實現(xiàn)。
FiberNode Tree的數(shù)據(jù)結(jié)構(gòu)如圖所示:
FiberNode 上有很多屬性,包括和自身相關(guān)的屬性 ref,節(jié)點之間的關(guān)系 return、silbing還有工作單元上的屬性,比如 pendingProps等等,后面會詳細介紹。
Fiber最主要的兩層含義:
Fiber的出現(xiàn)也為React帶來了很多意義:
從優(yōu)化層面來說,F(xiàn)iber是一種新的調(diào)和算法(reconciliation algorithm)。
這兩個概念會在后面的章節(jié)詳細講解。
Fiber是React的最小的工作單元。在React的世界中,一切都可以是組件。在普通的HTML頁面上,開發(fā)者們可以將多個DOM元素整合在一起組成一個組件。
普通的DOM元素(HostComponent)可以是組件,普通的文本節(jié)點(HostText)也可以是組件。還有通過ReactDom.render方法創(chuàng)建的根元素(RootElement)也可以是組件,還有經(jīng)常在React中使用的函數(shù)組件(FunctionComponent)。
在React源碼中,每個FiberNode都有一個WorkTag屬性,用于標識當前節(jié)點的類型。
// pagkages/react-reconciler/src/ReactWorkTags.ts
export type WorkTag =
| typeof FunctionComponent
| typeof ClassComponent
| typeof HostRoot
| typeof HostComponent
| typeof HostText
| typeof Fragment;
export const FunctionComponent = 0;
export const ClassComponent = 1;
export const HostRoot = 3; // 通過ReactDom.render()產(chǎn)生的根元素
export const HostComponent = 5; // dom元素 比如
export const HostText = 6; // 文本類型 比如:123
export const Fragment = 7; //
ReactWorkTags.ts文件中定義了所有可能的節(jié)點類型,每個類型都應(yīng)一個number類型的值。
這樣做的好處是可以通過比較兩個節(jié)點的 WorkTag 屬性來判斷它們是否是同一類型的節(jié)點,而不需要通過字符串比較等方式,這樣可以提高比較的效率,也可以減少出錯的可能性。
每一個組件都對應(yīng)著一個FiberNode?,許多個FiberNode?互相嵌套、關(guān)聯(lián)就組成了FiberNode Tree?。正如下面表示的FiberNode Tree和DOM樹的關(guān)系一樣:
Fiber樹 DOM樹
div#root div#root
| |
div
| / \
div p a
/ ↖
/ ↖
p ---->
|
a
一個DOM節(jié)點一定對應(yīng)著一個FiberNode,但每一個Fiber節(jié)點缺不一定有對應(yīng)的DOM節(jié)點。
因為React支持不同類型的組件,因此每個FiberNode并不一定具有對應(yīng)的DOM節(jié)點。
Fiber作為工作單元,它有很多屬性:
// pagkages/react-reconciler/src/ReactFiber.js
function FiberNode(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
) {
// Instance
this.tag = tag;
this.key = key;
this.type = null; // fiber對應(yīng)的DOM元素的標簽類型,div、p...
this.stateNode = null; // fiber的實例,類組件場景下,是組件的類,HostComponent場景,是dom元素
this.ref = null; // ref相關(guān)
// Fiber 除了有自身實例上的屬性,還需要有表示和其它節(jié)點的關(guān)系
this.return = null; // 指向父級fiber
this.child = null; // 指向子fiber
this.sibling = null; // 同級兄弟fiber
this.index = 0;
// 作為工作單元與Fiber更新相關(guān)
this.pendingProps = pendingProps; // 剛開始工作階段的 props
this.memoizedProps = null; // 工作結(jié)束時確定下來的 props
this.memoizedState = null; // 更新完成后的新 state
this.updateQueue = null; // Fiber產(chǎn)生的更新操作都會放在更新隊列中
// Effects
this.flags = NoFlags; // 比如插入 更改 刪除dom等)初始狀態(tài)時表示沒有任何標記(因為還沒進行fiberNode對比)
this.subtreeFlags = NoFlags; // 子節(jié)點副作用標識
this.deletions = null; // 用于存放被刪除的子節(jié)點
/*
* 可以看成是workInProgress(或current)樹中的和它一樣的節(jié)點,
* 可以通過這個字段是否為null判斷當前這個fiber處在更新還是創(chuàng)建過程
* */
this.alternate = null; // 用于 current Fiber樹和 workInProgress Fiber樹的切換(如果當時fiberNode樹是current樹,則alternate指向的是workInProgress樹)
}
這里Fiber節(jié)點的屬性沒有寫完全,可以去react源碼里看,地址在代碼塊首行。
雖然屬性很多,但可以按三層含義將它們分類來看:
每個Fiber節(jié)點有個對應(yīng)的React element,多個Fiber節(jié)點是如何連接形成樹呢?靠如下三個屬性:
// 指向父級Fiber節(jié)點
this.return = null;
// 指向子Fiber節(jié)點
this.child = null;
// 指向右邊第一個兄弟Fiber節(jié)點
this.sibling = null;
舉個例子,比如下面的組件結(jié)構(gòu):
function App() {
return (
i am
時光屋小豪
fighting
)
}對應(yīng)的FiberNode Tree結(jié)構(gòu):
作為靜態(tài)的數(shù)據(jù)結(jié)構(gòu),需要保存組件的相關(guān)的信息:
// Fiber對應(yīng)組件的類型 Function/Class/Host...
this.tag = tag;
// key屬性
this.key = key;
// 大部分情況同type,某些情況不同,比如FunctionComponent使用React.memo包裹
this.elementType = null;
// 對于 FunctionComponent,指函數(shù)本身,對于ClassComponent,指class,對于HostComponent,指DOM節(jié)點tagName
this.type = null;
// Fiber對應(yīng)的真實DOM節(jié)點
this.stateNode = null;
作為動態(tài)的工作單元,F(xiàn)iber中如下參數(shù)保存了本次更新相關(guān)的信息,會在后續(xù)的更新流程章節(jié)中使用到具體屬性時再詳細介紹。
// 保存本次更新造成的狀態(tài)改變相關(guān)信息
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// 保存本次更新會造成的DOM操作
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
如下兩個字段保存調(diào)度優(yōu)先級相關(guān)的信息,會在講解Scheduler時介紹。
// 調(diào)度優(yōu)先級相關(guān)
this.lanes = NoLanes;
this.childLanes = NoLanes;
在本節(jié)我們對Reconciler的架構(gòu)有了大概的認知,了解了傳統(tǒng)的庫與現(xiàn)代框架的工作原理,也掌握了預(yù)編譯和即時編譯的區(qū)別,以及它們在現(xiàn)代框架中的應(yīng)用。
在上一節(jié)中,我們實現(xiàn)了JSX的轉(zhuǎn)換,知道了React Element這種數(shù)據(jù),但是它也有一定的缺陷,為了解決這個缺陷,React 引入了Fiber架構(gòu),介紹了Fiber出現(xiàn)的意義,以及它的結(jié)構(gòu)是什么樣的,通過FiberNode組成的FiberNode Tree的結(jié)構(gòu)。
下一節(jié)主要介紹Fiber作為Reconciler核心模塊的工作單元,是如何創(chuàng)建及更新DOM的。

我們在微信上24小時期待你的聲音
解答本文疑問/技術(shù)咨詢/運營咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流