2024年12月5日,react發布了react19版本。后面一段時間都將學習它的源碼,并著手記錄。
react官網:react19新特性
https://react.dev/blog/2024/12/05/react-19
在用vite創建react項目的使用,main.tsx
主文件都會有以下代碼。
//import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.tsx'const root = createRoot(document.getElementById('root')!);
console.log('root',root)
root.render(// <StrictMode><App />// </StrictMode>,
)
該代碼是整個項目的入口。
接下來我們就看看 createRoot 是如何執行。
createRoot 函數
createRoot
函數的主要作用是創建一個 React 應用的根節點,并對其進行一系列初始化操作,最終返回一個 ReactDOMRoot
對象。
function createRoot(container: Element | Document | DocumentFragment,options?: CreateRootOptions,
): RootType {// 用于控制是否默認啟用并發更新const concurrentUpdatesByDefaultOverride = false;// 表示是否啟用嚴格模式等。let isStrictMode = false;//let identifierPrefix = '';let onUncaughtError = defaultOnUncaughtError;let onCaughtError = defaultOnCaughtError;let onRecoverableError = defaultOnRecoverableError;let transitionCallbacks = null;//省略 options處理代碼// 創建容器對象 FiberRootNode
// FiberRootNode 對象,它是 React 協調器的核心數據結構,代表了整個 React 應用的根節點。const root = createContainer(container,ConcurrentRoot,//ConcurrentRoot 表示使用并發模式進行渲染。null,isStrictMode,concurrentUpdatesByDefaultOverride,identifierPrefix,onUncaughtError,onCaughtError,onRecoverableError,transitionCallbacks,);//在dom container節點添加屬性標記// 調用 markContainerAsRoot 函數,在 DOM 容器節點上添加一些屬性標記,用于標識該節點是 React 應用的根節點。markContainerAsRoot(root.current, container);// 處理容器元素// 如果傳入的 container 是一個注釋節點,則將其父節點作為根容器元素;否則,直接使用 container 作為根容器元素。const rootContainerElement: Document | Element | DocumentFragment =container.nodeType === COMMENT_NODE //8 注釋節點? (container.parentNode: any): container;// 事件委托處理// 在div#root上綁定各種事件,包括捕獲和冒泡階段// 可以實現事件委托,提高事件處理的效率。listenToAllSupportedEvents(rootContainerElement);// 根據創建的 FiberRootNode 對象 root,創建并返回一個 ReactDOMRoot 對象。這個對象提供了 render 方法,用于將 React 組件渲染到根節點上。return new ReactDOMRoot(root);
}
createContainer 函數
調用 createFiberRoot
函數來創建一個 FiberRoot
對象并返回。
函數參數含義:
●containerInfo
:類型為 Container,通常是一個 DOM 容器元素(如 div),用于指定 React 應用要掛載到的實際 DOM 節點。
●tag
:類型為 RootTag,用于標記根節點的類型,比如 ConcurrentRoot 表示使用并發模式渲染。
●hydrationCallbacks
:類型為 null | SuspenseHydrationCallbacks,在服務端渲染時,用于處理水合(hydration)過程中的回調函數。
●isStrictMode
:布爾類型,用于指示是否開啟嚴格模式。嚴格模式會對組件進行額外的檢查,幫助開發者發現潛在問題。
●concurrentUpdatesByDefaultOverride
:這個參數目前已被忽略,未來可能會移除。
●identifierPrefix
:字符串類型,用于為 React 元素生成唯一標識符的前綴。
●onUncaughtError
:錯誤處理回調函數,當發生未捕獲的錯誤時會調用此函數。
●onCaughtError
:錯誤處理回調函數,當錯誤被捕獲時調用,會提供更多錯誤信息,包括錯誤邊界組件。
●onRecoverableError
:錯誤處理回調函數,當發生可恢復的錯誤時調用。
●transitionCallbacks
:類型為 null | TransitionTracingCallbacks,用于跟蹤過渡(transition)狀態的回調函數。
function createContainer(containerInfo: Container,// 實際的dom容器tag: RootTag,// 渲染類型 并發模式或者傳統模式hydrationCallbacks: null | SuspenseHydrationCallbacks,isStrictMode: boolean,concurrentUpdatesByDefaultOverride: null | boolean,identifierPrefix: string,onUncaughtError: (error: mixed,errorInfo: {+componentStack?: ?string},) => void,onCaughtError: (error: mixed,errorInfo: {+componentStack?: ?string,+errorBoundary?: ?React$Component<any, any>,},) => void,onRecoverableError: (error: mixed,errorInfo: {+componentStack?: ?string},) => void,transitionCallbacks: null | TransitionTracingCallbacks,
): OpaqueRoot {// 設置為 false 表示不進行水合操作。水合操作通常用于服務端渲染,將服務端渲染的 HTML 與客戶端的 React 應用進行連接。const hydrate = false;// 初始子元素,這里設置為 null,表示一開始沒有初始的子元素。const initialChildren = null;//創建 FiberRoot// createFiberRoot 函數創建 FiberRoot 對象,該函數會根據傳入的參數初始化 FiberRoot 的各種屬性,包括根節點的類型、掛載的 DOM 容器、錯誤處理回調等,最終返回一個代表整個 React 應用根節點的 FiberRoot 對象。return createFiberRoot(containerInfo,tag,// 模式 并發模式hydrate,initialChildren,hydrationCallbacks,isStrictMode,identifierPrefix,onUncaughtError,onCaughtError,onRecoverableError,transitionCallbacks,null,);
}
createFiberRoot 函數
createFiberRoot
函數是 React 中用于創建 FiberRoot
對象的核心函數,FiberRoot
是整個 React 應用的根節點,負責管理應用的渲染和更新流程。該函數會初始化根節點的各種屬性,創建對應的 Fiber
節點,并進行一些狀態和更新隊列的初始化操作。
函數參數含義:
●containerInfo
:通常是一個 DOM 容器元素,用于指定 React 應用要掛載到的實際 DOM 節點。
●tag
:標記根節點的類型,如 ConcurrentRoot 表示使用并發模式渲染。
●hydrate
:布爾值,指示是否進行水合操作(用于服務端渲染)。
●initialChildren
:初始的子元素列表。
●hydrationCallbacks
:在水合過程中使用的回調函數。
●isStrictMode
:布爾值,指示是否開啟嚴格模式。
●identifierPrefix
:用于為 React 元素生成唯一標識符的前綴。
●onUncaughtError
:未捕獲錯誤的處理回調函數。
●onCaughtError
:已捕獲錯誤的處理回調函數。
●onRecoverableError
:可恢復錯誤的處理回調函數。
●transitionCallbacks
:用于跟蹤過渡狀態的回調函數。
●formState
:表單狀態信息。
function createFiberRoot(containerInfo: Container,// 實際的dom容器tag: RootTag,// 渲染模式 并發模式或者傳統模式hydrate: boolean,initialChildren: ReactNodeList,hydrationCallbacks: null | SuspenseHydrationCallbacks,isStrictMode: boolean,// 示范嚴格模式identifierPrefix: string,// 前綴onUncaughtError: (error: mixed,errorInfo: {+componentStack?: ?string},) => void,onCaughtError: (error: mixed,errorInfo: {+componentStack?: ?string,+errorBoundary?: ?React$Component<any, any>,},) => void,onRecoverableError: (error: mixed,errorInfo: {+componentStack?: ?string},) => void,transitionCallbacks: null | TransitionTracingCallbacks,formState: ReactFormState<any, any> | null,
): FiberRoot {// 創建 FiberRootNode 實例// 創建一個 FiberRootNode 實例,該實例代表整個 React 應用的根節點,包含了與 DOM 容器、根節點類型、錯誤處理等相關的信息。const root: FiberRoot = (new FiberRootNode(containerInfo,tag,// 并發模式hydrate,identifierPrefix,onUncaughtError,onCaughtError,onRecoverableError,formState,): any);if (enableSuspenseCallback) {// 設置水合回調函數root.hydrationCallbacks = hydrationCallbacks;}if (enableTransitionTracing) {// 設置過渡跟蹤回調函數root.transitionCallbacks = transitionCallbacks;}// 創建 Fiber 節點,該節點代表根組件的 Fiber 節點。const uninitializedFiber = createHostRootFiber(tag, isStrictMode);//root.current 指向當前的 Fiber 節點,建立 FiberRoot 到 Fiber 節點的引用。root.current = uninitializedFiber;//uninitializedFiber.stateNode 指向 FiberRoot,建立 Fiber 節點到 FiberRoot 的引用。uninitializedFiber.stateNode = root;// 根據是否啟用緩存,初始化 Fiber 節點的狀態。狀態對象包含初始子元素、是否水合以及緩存信息。if (enableCache) {// 創建緩存對象const initialCache = createCache();// 保留緩存引用retainCache(initialCache);// 將緩存對象賦值給根節點的 pooledCache 屬性root.pooledCache = initialCache;// 再次保留緩存引用retainCache(initialCache);// 初始化根 Fiber 節點的狀態const initialState: RootState = {element: initialChildren,//存儲初始的子元素列表 initialChildren,這些子元素是 React 應用開始渲染時的初始內容。isDehydrated: hydrate,//一個布爾值,用于指示是否處于水合狀態,其值來源于 hydrate 參數。cache: initialCache,// 存儲前面創建的緩存對象 initialCache,這樣根 Fiber 節點就可以訪問這個緩存對象。};// 存儲前面創建的緩存對象 initialCache,這樣根 Fiber 節點就可以訪問這個緩存對象。// memoizedState 用于存儲 Fiber 節點的狀態,后續在渲染和更新過程中,React 會根據這個狀態來決定如何渲染組件。uninitializedFiber.memoizedState = initialState;} else {const initialState: RootState = {element: initialChildren,isDehydrated: hydrate,cache: (null: any), // not enabled yet};uninitializedFiber.memoizedState = initialState;}// 初始化更新隊列// 調用 initializeUpdateQueue 函數,為 Fiber 節點初始化更新隊列,用于管理后續的狀態更新操作。initializeUpdateQueue(uninitializedFiber);// 返回 FiberRoot 對象return root;
}
fiberRoot
節點與 fiber
節點的關系:
fiberRoot
的current
屬性 是根節點的fiber
對象的引用。
createHostRootFiber 函數
創建一個根 Fiber
節點,該節點代表整個 React 應用的根。在創建過程中,會根據不同的tag
模式(如傳統模式、并發模式)以及是否開啟嚴格模式和性能分析器等條件,來設置 Fiber
節點的 mode 屬性,最后調用 createFiber
函數完成 Fiber
節點的創建。
函數參數含義:
tag
,類型為 RootTag,用于標記根節點的類型。tag 的值可能為傳統模式(值為 0)或并發模式(ConcurrentRoot,值為 1)。isStrictMode
,布爾類型,指示是否開啟嚴格模式。嚴格模式會對組件進行額外的檢查,幫助開發者發現潛在問題。
function createHostRootFiber(tag: RootTag,isStrictMode: boolean,
): Fiber {//設置 mode 屬性,用于存儲 Fiber 節點的模式。let mode;//disableLegacyMode常量為trueif (disableLegacyMode || tag === ConcurrentRoot) {mode = ConcurrentMode; // 并發模式// 是否嚴格模式(只有開發環境與藕可能為true)// if (isStrictMode === true) {// mode |= StrictLegacyMode | StrictEffectsMode;// }} else {mode = NoMode;// NoMode默認為0 即傳統模式}// enableProfilerTimer表示啟用性能分析器,isDevToolsPresent表示開發者工具存在// if (enableProfilerTimer && isDevToolsPresent) {// 相當于 mode = mode | ProfileMode 例如 (1 |2) = 3// mode |= ProfileMode;// }//創建根 Fiber 節點,HostRoot常量為3return createFiber(HostRoot, null, null, mode);
}
initializeUpdateQueue 函數
initializeUpdateQueue
函數的功能是為一個 Fiber
節點初始化更新隊列。
UpdateQueue<State>
類型的對象queue
,這個對象包含了更新隊列的各種屬性:
baseState
:初始狀態,賦值為 fiber.memoizedState,memoizedState 存儲了當前 Fiber 節點的最新狀態。firstBaseUpdate
和lastBaseUpdate
:分別指向更新隊列中第一個和最后一個基礎更新,初始值都為 null。基礎更新是指那些已經被處理過,但由于某些原因(如優先級問題)還沒有應用到 baseState 上的更新。shared
:是一個包含共享信息的對象,其中:
pending
:指向待處理的更新鏈表的尾部,初始值為 null。lanes
:表示更新的優先級,初始值為 NoLanes,代表沒有任何優先級。hiddenCallbacks
:用于存儲隱藏的回調函數,初始值為 null。callbacks
:用于存儲更新完成后的回調函數,初始值為 null。
function initializeUpdateQueue<State>(fiber: Fiber): void {const queue: UpdateQueue<State> = {baseState: fiber.memoizedState,firstBaseUpdate: null,lastBaseUpdate: null,shared: {pending: null,lanes: NoLanes,hiddenCallbacks: null,},callbacks: null,};// 將創建好的更新隊列 queue 賦值給 fiber 節點的 updateQueue 屬性,這樣 Fiber 節點就有了自己的更新隊列,后續的狀態更新操作可以通過這個隊列來進行管理。fiber.updateQueue = queue;
}
ReactDOMRoot 構造函數
ReactDOMRoot
構造函數的主要作用是創建一個 ReactDOMRoot
實例,并將傳入的 FiberRoot
對象存儲在實例的 _internalRoot
屬性中。
function ReactDOMRoot(internalRoot: FiberRoot) {this._internalRoot = internalRoot;
}
ReactDOMRoot.prototype.render 函數
render
是ReactDOMRoot
原型上的一個方法,主要作用是在根 DOM 節點上渲染一個 React
元素。
ReactDOMHydrationRoot.prototype.render = ReactDOMRoot.prototype.render =// $FlowFixMe[missing-this-annot]function (children: ReactNodeList): void {const root = this._internalRoot;if (root === null) {throw new Error('Cannot update an unmounted root.');}//執行更新updateContainer(children, root, null, null);};
ReactDOMRoot.prototype.unmount 函數
unmount
方法是ReactDOMRoot
原型上的一個方法,其主要作用是卸載 React
應用的根節點,清理相關資源,將根節點從 DOM 中移除,釋放內存并確保應用不再占用相關資源。
ReactDOMHydrationRoot.prototype.unmount = ReactDOMRoot.prototype.unmount =// $FlowFixMe[missing-this-annot]function (): void {// 獲取內部根節點實例const root = this._internalRoot;// 存在if (root !== null) {// 清空根實例的引用this._internalRoot = null;// root.containerInfo 存儲了 React 應用渲染的 DOM 容器節點信息。通過 root.containerInfo 獲取根節點對應的 DOM 容器節點。const container = root.containerInfo;// 同步更新容器updateContainerSync(null, root, null, null);// 刷新同步工作flushSyncWork();// 取消容器的根節點標記unmarkContainerAsRoot(container);}};
全局變量
const randomKey = Math.random().toString(36).slice(2);//internalInstanceKey 用于在 DOM 元素上存儲 React Fiber 實例的相關信息。
const internalInstanceKey = '__reactFiber$' + randomKey;// internalPropsKey 用于存儲傳遞給組件的 props 信息。
const internalPropsKey = '__reactProps$' + randomKey;const internalContainerInstanceKey = '__reactContainer$' + randomKey;// internalEventHandlersKey 用于存儲事件處理函數的相關信息。
const internalEventHandlersKey = '__reactEvents$' + randomKey;
const internalEventHandlerListenersKey = '__reactListeners$' + randomKey;
const internalEventHandlesSetKey = '__reactHandles$' + randomKey;
const internalRootNodeResourcesKey = '__reactResources$' + randomKey;
const internalHoistableMarker = '__reactMarker$' + randomKey;
工具函數 markContainerAsRoot
markContainerAsRoot
的作用是將一個 DOM 容器節點標記為 React 應用的根容器,并將對應的 Fiber
根節點與該容器節點關聯起來。在 React 的渲染過程中,需要將 Fiber
樹與實際的 DOM
節點進行關聯。
函數參數含義:
hostRoot
, 根節點fibernode
, DOM 容器節點
function markContainerAsRoot(hostRoot: Fiber, node: Container): void {// $FlowFixMe[prop-missing]node[internalContainerInstanceKey] = hostRoot;// 變量internalContainerInstanceKey = '__reactContainer$' + randomKey;
}
工具函數 markContainerAsRoot
function unmarkContainerAsRoot(node: Container): void {// $FlowFixMe[prop-missing]node[internalContainerInstanceKey] = null;
}
流程圖
后續繼續看FiberRoot
節點和Fiber
節點的結構以及root.render
中的updateContainer
是如何執行的。