觸發渲染過程——renderRoot
renderRoot
是一個函數,用于觸發渲染工作。它通常會調用并遞歸地執行一系列的渲染任務,直到完成整個更新過程。這個過程包括執行 Fiber 樹中的 beginWork 和 completeWork,以及渲染新狀態或 DOM。
function renderRoot(root: FiberRootNode) {//雙緩存機制,將current復制一層給workInProgress//React 使用兩棵 Fiber 樹(current 和 workInProgress)來實現雙緩存const { current } = root; // 獲取當前的 Fiber 樹的根節點let workInProgress = current;// 啟動渲染任務(并發渲染模式下會啟動任務)workInProgress = performUnitOfWork(workInProgress);// 繼續調度工作單元while (workInProgress !== null) {workInProgress = performUnitOfWork(workInProgress);}
}
react源碼解析8.render階段
renderRoot
React 的渲染過程可以分為多個階段,包括:更新(Reconciliation)、渲染(Rendering)、提交(Commit)等。
//更細節的
function renderRoot(root: FiberRootNode) {try {// 初始化prepareFreshStack(root);// 開始工作循環do{try{workLoop();break;} catch (e) {console.warn('workLoop發生錯誤', e);workInProgress = null;}while (true); // 獲取完成的工作單元const finishedWork = root.current.alternate;root.finishedWork = finishedWork; // 提交根節點的wip fiberNode樹,并處理flagscommitRoot(root);
prepareFreshStatck()
此prepareFreshStatck()
中的createWorkInProgress簡單理解就是上面將current復制一層給workInProgress, const { current } = root; // 獲取當前的 Fiber 樹的根節點 let workInProgress = current;
,兩棵 Fiber 樹來實現雙緩存
。
復制過后的雙緩存 數據結構
workLoop()
function workLoop(){while(workInProgress !== null)performUnitOfWork(workInProgress);
}
performUnitOfWork(workInProgress) 【遞歸】
負責遞歸遍歷 Fiber 樹,并根據不同的 Fiber 類型執行相應的更新或渲染邏輯
function performUnitOfWork(fiber: FiberNode) {// 開始對當前Fiber節點進行工作// 這里的“遞”可能指的是遞歸處理子節點const next = beginWork(fiber); // 執行beginWork函數,返回下一個需要處理的Fiber節點// 更新當前Fiber節點的memoizedProps為pendingProps// 這通常意味著將傳入的props“確認”為當前節點的屬性fiber.memoizedProps = fiber.pendingProps;// 判斷下一個需要處理的節點是否為null// 如果為null,可能表示當前節點沒有子節點或子節點已經處理完畢if (next === null) {// 這里的“歸”可能指的是回溯到父節點,完成當前節點的工作completeUnitOfWork(fiber); // 執行completeUnitOfWork函數,完成當前Fiber節點的工作} else {// 如果還有下一個節點需要處理,則更新workInProgress指針// workInProgress通常指向當前正在處理的Fiber節點workInProgress = next; // 更新workInProgress為下一個需要處理的Fiber節點// 注意:這里通常會有一個遞歸調用performUnitOfWork(next),但在您提供的代碼片段中省略了}
}
遞歸結束
,狀態模型
:
beginWork流程(遞)
- 建立節點的父子以及兄弟節點關聯關系
child return sibling
屬性 - 給fiber節點打上
flag標記
(當前節點的flag)
beginWork 主要發生在協調階段,它被調用來處理和更新 React 虛擬 DOM(Fiber 樹)。根據節點類型(HostRoot、FunctionComponent等)調用對應更新函數
1.初始化 Fiber 節點的工作:當 React 開始處理某個 Fiber 節點時,beginWork 會被調用。它會檢查該節點的當前狀態,并決定是否需要進行更新。
2.調度子節點的更新:如果當前節點有子節點,beginWork 會為這些子節點安排進一步的更新工作。
在執行過程中,beginWork 會遍歷 Fiber 樹,判斷是否需要更新(例如,檢查 props 或 state 是否發生了變化),然后決定是否繼續向下渲染(即繼續對子節點調用 beginWork)。
export const beginWork = (wip: FiberNode): FiberNode | null => {// 根據Fiber節點的類型,執行相應的更新邏輯,并返回下一個需要處理的Fiber節點switch (wip.tag) {case 'HostRoot':return updateHostRoot(wip); // 處理根節點case 'HostComponent':return updateHostComponent(wip); // 處理宿主組件(如DOM元素)case 'HostText':return null; // 文本節點不需要進一步處理,直接返回nullcase 'FunctionComponent':return updateFunctionComponent(wip); // 處理函數組件default:console.warn('beginWork未實現的類型'); // 輸出警告信息break; // 注意:這里的break是多余的,因為return已經會退出函數// 但為了保持格式一致性和清晰性,我們暫時保留它return null; // 對于未實現的類型,返回null}
beginWork初次執行完, 此時內存狀態,wip和h1對應的fiber對象建立聯系,并且給h1 fiber打上flags標記。
updateHostRoot
更新隊列(processUpdateQueue【memorizedState=element元素】)、協調子元素(reconcileChildren)
function updateHostRoot(wip: FiberNode): FiberNode | null {// 獲取當前工作單元(Fiber)的基態const baseState = wip.memoizedState as Element; // 假設memoizedState是Element類型// 獲取更新隊列,并斷言其類型為UpdateQueue<Element>const updateQueue = wip.updateQueue as UpdateQueue<Element>;// 從共享對象中取出待處理的更新,并清空待處理隊列const pending = updateQueue.shared.pending;updateQueue.shared.pending = null;// 使用processUpdateQueue函數處理待處理的更新,并獲取更新后的狀態//執行函數獲取element對象const { memoizedState } = processUpdateQueue(baseState, pending) as { memoizedState: Element };// 更新當前工作單元的基態為最新狀態wip.memoizedState = memoizedState;// 獲取更新后的子節點,這里假設memoizedState直接代表了子節點// 注意:這里的邏輯可能需要根據實際情況調整,因為memoizedState可能并不直接等于子節點const nextChildren = wip.memoizedState as Element[]; // 假設這里是Element數組,但需要根據實際情況確定// 調用reconcileChildren函數來協調(渲染)子節點// 注意:函數名可能是reconcileChildren的一個拼寫錯誤,通常應該是reconcileChildren或者類似的名稱,但這里按照您提供的名稱使用reconcileChildren(wip, nextChildren);// 返回當前工作單元的第一個子節點,以便后續的工作單元可以繼續處理// 注意:如果wip.child是null,則表示沒有子節點需要處理return wip.child;
}
processUpdateQueue
export const processUpdateQueue = <state>(baseState: state,pendingUpdate: Update<state> | null
): { memoizedState: state } => {// 初始化結果對象,其memoizedState屬性設置為baseStateconst result: { memoizedState: state } = {memoizedState: baseState};// 檢查是否有待處理的更新if (pendingUpdate !== null) {const action = pendingUpdate.action;// 如果action是一個函數,則執行它并更新memoizedStateif (typeof action === 'function') {result.memoizedState = action(baseState);} else {// 如果action不是函數,則直接將其值賦給memoizedState// 注意:這里假設action的類型與state兼容result.memoizedState = action as state; // 需要類型斷言來確保TypeScript不會報錯}}// 返回結果對象return result;
};
** 注:Fiber對象數據結構 **
reconcileChildren(★★★)
reconcileChildren 主要處理組件的子樹,對于每一個子節點(即子 Fiber 節點)會執行以下操作:
子節點的類型判斷
(會首先判斷每個子節點的類型【比如是 DOM 元素、函數組件還是類組件等】,然后根據不同的類型來決定如何處理)節點的比較
(相同類型的節點/不同類型的節點/key 和索引
)生成新的 Fiber 節點
(為需要更新或新創建的子組件生成新的 Fiber 節點)處理子樹的遞歸(beginWork中遞歸調用)
(會遞歸地調用自己來處理子組件。如果某個子組件有子節點,React 會繼續對子節點進行協調,直到所有節點都被處理完)
reconcileChildFibers|mountChildFibers
創建子fiber的過程會進入reconcileChildren,該函數的作用是為workInProgress fiber節點生成它的child fiber即 workInProgress.child。然后繼續深度優先遍歷它的子節點執行相同的操作。mountChildFibers,reconcileChildFibers和mountChildFibers最終其實就是ChildReconciler
傳遞不同的參數返回的函數,這個參數用來表示是否追蹤副作用
.
function ChildReconciler(shouldTrackSideEffects) {function placeChild(newFiber, lastPlacedIndex, newIndex) {newFiber.index = newIndex;if (!shouldTrackSideEffects) {//是否追蹤副作用// Noop.return lastPlacedIndex;}var current = newFiber.alternate;if (current !== null) {var oldIndex = current.index;if (oldIndex < lastPlacedIndex) {// This is a move.newFiber.flags = Placement;return lastPlacedIndex;} else {// This item can stay in place.return oldIndex;}} else {// This is an insertion.newFiber.flags = Placement;return lastPlacedIndex;}}
}
const App:any=function (){return(<h1><h2><h3>3333</h3></h2></h1>)
}
初次被調用執行, 此時內存狀態,wip和h1對應的fiber對象建立聯系,并且給h1 fiber打上flags標記。
遞歸,直至next指向為null
completeWork流程(歸)
主要執行任務:
1.創建真實dom節點
,但是仍在內存中,未渲染到頁面
2.處理flag與subtreeFlags
(標記
子樹標識,用“|”
運算處理)
3.建立真實DOM關系
,將子元素插入父元素中
function completeWork(current, workInProgress) {switch (workInProgress.tag) {case 'HostComponent': {// 如果是普通的 DOM 節點if (!workInProgress.stateNode) {// 如果沒有對應的 DOM 實例,創建一個新的const domElement = document.createElement(workInProgress.type);// 為 DOM 元素添加屬性const props = workInProgress.pendingProps;for (const key in props) {if (key === 'children') {// 如果是文本內容,直接設置if (typeof props[key] === 'string' || typeof props[key] === 'number') {domElement.textContent = props[key];}} else if (key.startsWith('on')) {// 添加事件監聽器(如 onClick)const eventType = key.toLowerCase().substring(2);domElement.addEventListener(eventType, props[key]);} else {// 設置其他屬性domElement.setAttribute(key, props[key]);}}// 將 DOM 實例存儲在 stateNode 中workInProgress.stateNode = domElement;}return null;}case 'FunctionComponent':case 'ClassComponent': {// 函數組件和類組件在 completeWork 中通常不需要特殊處理return null;}default:return null;}
}