[FE] React 初窥门径(五):React 组件的加载过程(commit 阶段)

1. 回顾

前一篇文章我们看到,ReactDOM.render 总共包含这些步骤,

然后介绍了 performSyncWorkOnRoot 做的事情,它主要做了两件事,

  • renderRootSync 可称之为 render 阶段:创建了一颗 Fiber Tree(包含 html fragment 并未挂在到 DOM 中)
  • commitRoot 可称之为 commit 阶段:将 Fiber Tree 实际写入 DOM

前文我们重点介绍了 render 阶段 的业务逻辑,这个阶段由 renderRootSync 来完成,

其中 markRenderStarted 和 markRenderStopped 标志了 render 阶段 的开始和结束。

复制代码
[6] performSyncWorkOnRoot                                   <- 已创建好了 一个 FiberRootNode 和 两个 FiberNode(下文解释)
    [7] renderRootSync
        [8] markRenderStarted                               <- 开始 render 阶段
        [8] workLoopSync                                    <- 从上到下处理 FiberTree
            [9] performUnitOfWork ---- [HostRoot {tag: 3}]  <- 处理 WIP FiberNode
                [10] beginWork$1
                    [11] beginWork
                        [12] updateHostRoot
                            [13] reconcileChildren          <- 创建 child FiberNode
            [9] performUnitOfWork ---- [IndeterminateComponent {tag: 2}] (<App />)  <- 处理 child FiberNode
                [10] beginWork$1
                    [11] beginWork
                        [12] mountIndeterminateComponent
                            [13] renderWithHooks
                                [14] Component
                            [13] reconcileChildren          <- 创建 child FiberNode
            [9] performUnitOfWork ---- [HostText {tag: 6}] ('hello world')          <- 处理 child FiberNode
                [10] beginWork$1
                    [11] beginWork
                        [12] updateHostText
                [10] completeUnitOfWork                     <- 从下到上处理 FiberTree
                    [11] completeWork ---- [HostText {tag: 6}] ('hello world')
                        [12] createTextInstance
                            [13] createTextNode
                                [14] createTextNode [HTMLElement] ('hello world')
                    [11] completeWork ---- [IndeterminateComponent {tag: 2}] (<App />)
                    [11] completeWork ---- [HostRoot {tag: 3}]
        [8] markRenderStopped                               <- 结束 render 阶段
    [7] commitRoot
        [8] runWithPriority$1
            [9] reactPriorityToSchedulerPriority
            [9] Scheduler_runWithPriority
                    [11] markCommitStarted
                    ?                                       <- 本文重点介绍这里
                    [11] markCommitStopped
    [7] ensureRootIsScheduled

需知在 performSyncWorkOnRoot 之前,React 已经创建好了 3 跟 Fiber 节点,

  • 一个 FiberRootNode,tag 为 LagacyRoot:它的 containerInfo 指向了 div#root 这个 DOM 元素
  • 两个互为 alternate 的 FiberNode,tag 为 HostRoot:它的 stateNode 指向了上面那个 FiberRootNode

我们来画一下它们的关系,

从图中可以看出 render 阶段 做的事情就是创建 FiberTree,

为一个标记为 WIP(workInProgress 变量)的 FiberNode 创建了多个子孙元素。

(在 render 阶段 执行过程中,workInProgress 这个全局变量的值会变,图中画的是它的初始值)

为了描述方便,图中略过了很多内容,例如 FiberNode 中的 updateQueue 等等。

2. 在大图中的位置

本文开始介绍 commit 阶段 的业务逻辑,

VSCode 插件 CodeTour 的安装可以参考 前一篇 文章,

相关仓库在这里 github: thzt/react-tour。

ReactDOM.render 的全流程在这里,
4.1.1 组件加载过程:函数组件(全流程)

我们要看这部分内容,即 commitRoot 的调用过程,

3. commitRoot 业务逻辑

一图胜千言,(函数前面的数字,表示缩进层次)

复制代码
[7] commitRoot
    [8] runWithPriority$1
        [9] Scheduler_runWithPriority
            [10] eventHandler=commitRootImpl
                [11] markCommitStarted
                [11] invokeGuardedCallback                           <- 第一步
                    [12] invokeGuardedCallbackImpl$1
                        [13] dispatchEvent
                            [14] func=commitBeforeMutationEffects
                                [15] commitBeforeMutationLifeCycles
                                    [16] clearContainer              <- div#root 的 textContext 置空
                [11] invokeGuardedCallback                           <- 第二步(这里要注意,看下文)
                    [12] invokeGuardedCallbackImpl$1
                        [13] dispatchEvent
                            [14] func=commitMutationEffects
                                [15] commitPlacement
                                    [16] insertOrAppendPlacementNodeIntoContainer     <- 把 FiberNode <App /> 放到 div#root 中
                                        [17] insertOrAppendPlacementNodeIntoContainer <- 这是个递归函数,找到后代节点 stateNode 指向的 #text
                                            [18] appendChildToContainer
                                                [19] appendChild                      <-  实际操作 DOM,之后页面立即展示效果
                [11] invokeGuardedCallback                           <- 第三步
                    [12] invokeGuardedCallbackImpl$1
                        [13] dispatchEvent
                            [14] func=commitLayoutEffects
                [11] markCommitStopped

我们看到 commitRoot 总共包含了三个步骤:

  • commitBeforeMutationEffects:将 div#root 置空
  • commitMutationEffects:将 #text 实际写入 DOM 中
  • commitLayoutEffects:当前示例,这一块没有特殊要说明的事情

commit 阶段 用 markCommitStarted 和 markCommitStopped 标记了开始和结束。

值得注意的是,commit 阶段 第三步 commitLayoutEffects 之前,

React 会将 FiberRootNode 的 current 属性指向创建好了 Fiber Tree。

(总共有两个 Fiber Tree,组件加载过程只创建一个)

如图所示,


参考

React 初窥门径(四):React 组件的加载过程(render 阶段)
VSCode: CodeTour
github: thzt/react-tour
4.1.1 组件加载过程:函数组件(全流程)
最后编辑于:2025-02-24 21:39:58
© 著作权归作者所有,转载或内容合作请联系作者

喜欢的朋友记得点赞、收藏、关注哦!!!

相关推荐
Joy T3 小时前
Agent 确定性上下文设计:从 Prompt 参数注入到能力分层
java·开发语言·微服务·架构·prompt·agent context
AIgorithmGEEK6 小时前
MySQL 索引全解(下):建、查、删实战与知识速查表
数据库·mysql·b+树·索引
Lost_the_wind6 小时前
MySQL学习笔记四
mysql
m0_587383006 小时前
广州 24 小时自助健身房解决方案技术实现与实战指南
java·spring·小程序·架构·需求分析
·云扬·6 小时前
Prompt 工程与安全:从 System 设计到注入防御
java·人工智能·安全·ai·prompt
代码不停7 小时前
自动化测试(java)
java·开发语言·selenium·自动化
志尊宝8 小时前
Vue3 零基础每日笔记(082):文件上传实战——el-upload 校验、自定义上传、头像预览
前端·javascript·vue.js·笔记·#javascript·#vue.js
鬼手点金9 小时前
opencode-rules 插件
java·开发语言·前端·人工智能·chrome·git·github
paopaokaka_luck10 小时前
滑雪场管理系统(AI智能助手流式对话,ECharts 运营数据分析、教练、雪具与场地一体化预约,钱包支付退款、订单核销与服务评价)
java·人工智能·spring boot·网络协议·数据分析·echarts
SL_staff10 小时前
JVS混合架构实践:能力引擎+动态模型如何支撑企业级可持续交付
spring boot·低代码·全栈