React19源码系列之createRoot的执行流程是怎么的?

server/2025/3/16 22:39:19/

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节点的关系:
fiberRootcurrent属性 是根节点的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 节点的最新状态。
  • firstBaseUpdatelastBaseUpdate:分别指向更新队列中第一个和最后一个基础更新,初始值都为 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 函数

renderReactDOMRoot原型上的一个方法,主要作用是在根 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, 根节点fiber
  • node, 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是如何执行的。


http://www.ppmy.cn/server/175544.html

相关文章

08 | 实现版本号打印功能

提示&#xff1a; 所有体系课见专栏&#xff1a;Go 项目开发极速入门实战课&#xff1b;欢迎加入 云原生 AI 实战 星球&#xff0c;12 高质量体系课、20 高质量实战项目助你在 AI 时代建立技术竞争力&#xff08;聚焦于 Go、云原生、AI Infra&#xff09;&#xff1b;本节课最终…

颠覆语言认知的革命!神经概率语言模型如何突破人类思维边界?

颠覆语言认知的革命&#xff01;神经概率语言模型如何突破人类思维边界&#xff1f; 一、传统模型的世纪困境&#xff1a;当n-gram遇上"月光族难题" 令人震惊的案例&#xff1a;2012年Google语音识别系统将 用户说&#xff1a;“我要还信用卡” 系统识别&#xff…

JavaScript相关面试题

以下是150道JavaScript相关面试题及详细答案&#xff1a; JavaScript基础 1.JavaScript是什么&#xff1f; JavaScript是一种直译式脚本语言&#xff0c;主要用于网页开发&#xff0c;也可用于服务器端开发&#xff08;如Node.js&#xff09;。它是一种动态类型、弱类型、基于原…

FPGA初级项目9——基于SPI的ADC芯片进行模数转换

FPGA初级项目9——基于SPI的ADC芯片进行模数转换 ADC芯片介绍 ADC&#xff08;Analog-to-Digital Converter&#xff09;芯片是一种将连续变化的模拟信号转换为离散数字信号的电子器件&#xff0c;广泛应用于电子系统中&#xff0c;是连接现实世界与数字世界的桥梁。可将电压、…

骑士74CMS_v3.34.0SE版uniapp全开源小程序怎么编译admin和member流程一篇文章说清楚

有粉丝一直问我骑士系统怎么编译后台和小程序目前骑士人才系统74CMS分标准版&#xff0c;创业板&#xff0c;专业版&#xff0c;其除功能不同外其配置方法完全一致有喜欢系统的也可以私信我或者找我获取 一.安装打包环境[Nodejs]这个就不用我说了吧&#xff0c;用不小于V20的版…

hadoop集群配置-scp的使用

1.推送。在hadoop100机器上&#xff0c;把文件推送到hadoop101机器上 在101和102中新建文件夹 在100中输入命令将jdk拷贝到101中 scp -r jdk1.8.0_212/ roothadoop101:/opt/module 用同样的方法拷贝hadoop scp -r hadoop-3.1.3/ roothadoop101:/opt/module 2.拉取。在hadoop1…

零信任架构实战手册-企业安全升级

🔐 开篇痛点暴击: “又被黑客钓鱼了?VPN漏洞补到心累?😫” 传统边界安全像纸糊的墙,内鬼渗透、APT攻击防不胜防! 别慌!零信任架构(Zero Trust)用「永不信任,持续验证」原则,让安全等级飙升10倍! 🚦 零信任3大核心武器(附实操步骤): 1. 🌟 身份即边界!抛…

玩转python:通俗易懂掌握高级数据结构-collections模块之ChainMap

引言 ChainMap是Python中collections模块提供的一个强大工具&#xff0c;用于将多个字典或映射合并为一个逻辑视图。它允许我们在不修改原始字典的情况下&#xff0c;对多个字典进行统一的查找和操作。ChainMap非常适合用于配置管理、多层作用域模拟等场景。本文将详细介绍Cha…