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

1. 回顾

前几篇文章中,我们采用了 VSCode 插件 CodeTour 来记录代码的执行过程,
并把相关的数据 .tour/ 放到了 github: thzt/react-tour 中。

截止到本文为之,我们总共记录了这些 code-tour,

.tour/
├── 2. 构建过程.tour
├── 3.1 react 的加载过程.tour
├── 3.2 react-dom 的加载过程.tour
├── 4.1 组件加载过程:函数组件(call stack).tour
├── 4.1.1 组件加载过程:函数组件(全流程).tour
└── 4.2 组件加载过程:类组件(call stack).tour

本文重点介绍 4.1.1 组件加载过程:函数组件(全流程) 相关的内容。

2. 极简的示例项目

现在我们开始介绍 React 函数组件的加载全流程,我们的示例项目如下,
github: thzt/react-tour/example-project

example-project/
├── README.md
├── package.json
├── public
|  └── index.html
├── src
|  ├── App.js
|  └── index.js
└── yarn.lock

其中,index.js 的内容如下,

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

App.js 的内容如下,

const App = () => {
  debugger;
  return 'hello world'
};

export default App;

当前 React 项目只加载了一个 <App /> 组件,这个组件只返回了一段纯文本 'hello world'

与 React 初窥门径(一):环境准备 介绍的一致,我们可以启动项目,

$ yarn
$ yarn start

# http://127.0.0.1:3000

3. 调试 Web 项目

参考 React 初窥门径(三):用 VSCode 调试,
我们将 github: thzt/react-tour 中的文件,拷贝到 React 源码根目录,

  • package.json:直接覆盖,其中新增了 debug-build 这个 npm scripts
  • .vscode/:拷贝到 React 源码目录,其中包含了两个 debug 配置,我们要用 Debug React 这个配置
  • .tour/:VSCode CodeTour 插件的数据

整体操作流程如下:
(1)示例项目操作过程

  • 下载 github: thzt/react-tour/example-project 示例项目
  • 给示例项目安装依赖:yarn
  • 替换 example-projectnode_modules 中的依赖 reactreact-dom
    参考 React 初窥门径(一):环境准备
    可使用工具 github: thzt/react-tour/tool/link.bash
  • 启动示例项目:yarn starthttp://127.0.0.1:3000

(2)React 源码操作过程

  • 下载(克隆) React 源码,并切换到 v17.0.2
  • 拷贝 github: thzt/react-tour 中的 package.json .vscode/ .tour/ 到 React 源码目录
  • 选择 Debug React 选项进行调试

我们发现 VSCode 的断点停在了 example-project/src/App.js 文件中。

const App = () => {
  debugger;              // <- 断点到了这里
  return 'hello world'
};

export default App;

4. 调用栈

我们先来跟踪一下,从 ReactDOM.render 到 App 组件 debugger 位置的调用栈,

在 CodeTour(.tour/)中,也记录了这个过程,

4.1 组件加载过程:函数组件(call stack)

render
legacyRenderSubtreeIntoContainer
unbatchedUpdates
fn
updateContainer
scheduleUpdateOnFiber
performSyncWorkOnRoot
renderRootSync
workLoopSync
performUnitOfWork
beginWork$1
beginWork
mountIndeterminateComponent
renderWithHooks

以上调用栈只展示了函数的链式调用关系,如果用缩进表示调用链路的话,它应该是这样的,

render
  legacyRenderSubtreeIntoContainer
    unbatchedUpdates
      fn
        updateContainer
          scheduleUpdateOnFiber
            performSyncWorkOnRoot
              renderRootSync
                workLoopSync
                  performUnitOfWork
                    beginWork$1
                      beginWork
                        mountIndeterminateComponent
                          renderWithHooks

它表示 render 调用了 legacyRenderSubtreeIntoContainer
legacyRenderSubtreeIntoContainer 又调用了 unbatchedUpdates
unbatchedUpdates 又调用了 fn 等等,直到最后调用了 renderWithHooks

最后 renderWithHooks 调用了函数组件 App,来到断点那里。

5. 全流程

只看调用栈的话,React 组件的加载过程还不完整,我们知道某个函数之前别调用之前,是否还调用了其他函数,
以下我们整理了从 renderApp 调用的全流程(函数前面的数字,表示缩进层次)。

4.1.1 组件加载过程:函数组件(全流程)
(下图包含代码折叠,而且只截了一部分,完整版请查看上面的链接)

6. render 和 commit 阶段

全流程包含了特别多的细碎逻辑,我们首先想弄明白的是,

  • 组件是何时被调用的,组件返回之后发生了什么(render 阶段)
  • 组件是如何展示在页面上的(commit 阶段)

这两个阶段,就是 performSyncWorkOnRoot 做的事情了,在大图中它处于这个位置,

可以看到:

  • render 阶段(renderRootSync:根据用户创建的 React 组件,创建 Fiber Tree(先从上到下 performUnitOfWork ,再从下到上 completeWork
  • commit 阶段(commitRoot:把 Fiber Tree 实际写入到 DOM 中

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

[6] performSyncWorkOnRoot
  [7] renderRootSync
    [8] markRenderStarted                                         <- render 阶段开始
    [8] workLoopSync
      [9] performUnitOfWork ---- [HostRoot {tag: 3}]              <- 从 根元素 开始向下构建 Fiber Tree
        [10] beginWork$1
          [11] beginWork
            [12] updateHostRoot                                   <- 加载 根元素 HostRoot
              [13] reconcileChildren
                [14] reconcileChildFibers                         <- 创建 child 子元素
                  [15] reconcileSingleElement
                    [16] createFiberFromElement
                      [17] createFiberFromTypeAndProps
                        [18] createFiber
      [9] performUnitOfWork ---- [IndeterminateComponent {tag: 2}] (<App />)
        [10] beginWork$1
          [11] beginWork
            [12] mountIndeterminateComponent                      <- 加载 函数组件 App
              [13] renderWithHooks
                [14] Component
              [13] reconcileChildren
                [14] mountChildFibers=reconcileChildFibers        <- 创建 child 子元素
                  [15] reconcileSingleTextNode
                    [16] deleteRemainingChildren
                    [16] createFiberFromText
                      [17] createFiber
      [9] performUnitOfWork ---- [HostText {tag: 6}] ('hello world')
        [10] beginWork$1
          [11] beginWork
            [12] updateHostText                                   <- 加载 纯文本 'hello world'
        [10] completeUnitOfWork                                   <- 开始倒着从 子节点 向上到 根节点 进行梳理
          [11] completeWork ---- [HostText {tag: 6}] ('hello world')
            [12] createTextInstance
              [13] createTextNode
                [14] createTextNode [HTMLElement] ('hello world') <- 实际创建 HTML
          [11] completeWork ---- [IndeterminateComponent {tag: 2}] (<App />)
          [11] completeWork ---- [HostRoot {tag: 3}]
    [8] markRenderStopped                                         <- render 阶段结束
  [7] commitRoot                                                  <- commit 阶段开始

下文我们再仔细介绍 commit 阶段。


参考

github: facebook/react v17.0.2
VSCode: CodeTour
github: thzt/react-tour
github: thzt/react-tour/example-project
React 初窥门径(一):环境准备
React 初窥门径(三):用 VSCode 调试
4.1 组件加载过程:函数组件(call stack)
4.1.1 组件加载过程:函数组件(全流程)

最后编辑于:2024-10-27 15:38:40


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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/907898.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

什么是数字签名技术?

信息安全五要素 名称说明机密性机密性是指网络信息不泄露给非授权的用户、实体或程序&#xff0c;能够防止非授权者获取信息完整性完整性是指网络信息或系统未经授权不能进行更改的特性可用性可用性是指合法许可的用户能够及时获取网络信息或服务的特性可控性可控性是指可以控…

人工智能原理实验一:知识的表示与推理实验

一、实验目的 本实验课程是计算机、智能、物联网等专业学生的一门专业课程&#xff0c;通过实验&#xff0c;帮助学生更好地掌握人工智能相关概念、技术、原理、应用等&#xff1b;通过实验提高学生编写实验报告、总结实验结果的能力&#xff1b;使学生对智能程序、智能算法等有…

虚拟机 Email 恢复专用工具:Virtual Machine Email Recovery

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据恢复、数据备份解决方案与服务&#xff0c;并针对企业面临的数据安全风险&#xff0c;提供专业的相关数据安全培训。 天津鸿萌科贸发展有限公司是 SysTools 系列数据恢复、取证及…

HTML 基础标签——表单标签<form>

文章目录 1. `<form>` 标签:定义表单容器2. `<input>` 标签:多用途输入控件3. `<textarea>` 标签:多行文本输入框4. `<select>` 标签:下拉选择框5. `<option>` 标签:下拉菜单选项6. `<button>` 标签:按钮元素7. `<label>` 标签…

使用Jupyter Notebook进行数据科学项目

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Jupyter Notebook进行数据科学项目 Jupyter Notebook 简介 安装 Jupyter Notebook 创建和管理 Notebook 编写和运行代码 示例…

详解RabbitMQ三种队列类型

RabbitMQ 是一个强大的消息队列系统&#xff0c;它提供了多种队列类型以满足不同的使用需求。本文将探讨三种主要队列类型&#xff1a;经典队列、仲裁队列和流式队列&#xff0c;并讨论它们的区别和选型建议。 经典队列&#xff08;Classic Queues&#xff09; 简介&#xff…

【AD】2-1 元件符号的绘制创建实例-电阻容/CHIP类器件

1.新建工程后&#xff0c;双击原理图库&#xff0c;点击Panels后&#xff0c;选择SCH Library&#xff0c;双击元器件可在右侧进行更改名称 2.点击视图&#xff0c;栅格&#xff0c;设置捕捉栅格为100mil 3.点击放置管脚&#xff0c;可按空格键进行旋转&#xff0c;按TAB键可以…

JDBC2(防止sql注入,数据库连接池)

防止SQL注入 sql注入&#xff1a;利用sql语句的语法特点&#xff0c;应用层输入特殊格式&#xff0c;让原有的sql语句失效 创建表结构 并加入数据 create table login(lid int primary key auto_increment,lname varchar(20),lpwd varchar(20),lsex varchar(2),laddr varcha…

基于SSM+微信小程序的订餐管理系统(点餐2)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1、项目介绍 基于SSM微信小程序的订餐管理系统实现了管理员和用户。管理端实现了 首页、个人中心、用户管理、菜品分类管理、菜品信息管理、订单信息管理、配送信息管理、菜品评价管理、订单投诉管理、…

MRCTF2020:你传你ma呢

文件上传题先判断黑白名单过滤&#xff0c;先传个最简单的木马 这里上传不了php文件&#xff0c;猜测可能是对php文件进行了过滤&#xff0c;将文件改为任意后缀这里改为.abc 还是上传不成功&#xff0c;猜测可能对MIME也做了过滤&#xff0c;将Content-Type更改为image/jpeg再…

设计模式09-行为型模式2(状态模式/策略模式/Java)

5.4 状态模式 5.4.1 状态模式的定义 1.模式动机&#xff1a;有些对象具有多种状态&#xff0c;这些状态在某些情况下能够相互转换&#xff0c;对象在不同的状态下将具有不同的行为&#xff0c;将拥有状态的对象中和状态的行为分离。 2.模式定义&#xff1a;允许一个对象在其…

tauri中shell的特殊字符

tauri中shell的特殊字符 官网例子&#xff1a;https://tauri.app/plugin/shell/ 中的入参是 \S 入参&#xff0c;但如果入参存在空格等特殊字符串&#xff0c;将无法传入 "permissions": [{"identifier": "shell:allow-execute","allow&qu…

电子电气架构 --- Trace 32(劳特巴赫)多核系统的调试

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 所有人的看法和评价都是暂时的,只有自己的经历是伴随一生的,几乎所有的担忧和畏惧,都是来源于自己的想象,只有你真的去做了,才会发现有多快乐。…

使用Web Workers实现JavaScript的多线程编程

&#x1f493; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4dd; Gitee主页&#xff1a;瑕疵的gitee主页 ⏩ 文章专栏&#xff1a;《热点资讯》 使用Web Workers实现JavaScript的多线程编程 引言 Web Workers 简介 创建和使用 Worker 通信机制 主线程向 Worker 发送消息 Wor…

【uni-app】创建自定义模板

1. 步骤 打开自定义模板文件夹 在此文件夹下创建模板文件&#xff08;注意后缀名&#xff09; 重新点击“新建页面” 即可看到新建的模板 2. 注意事项 创建的模板必须文件类型对应&#xff08;vue模板就创建*.vue文件, uvue模板就创建*.uvue文件&#xff09;

【云原生】Docker搭建开源翻译组件Deepl使用详解

目录 一、前言 二、微服务项目使用翻译组件的场景 2.1 多语言用户界面 2.2 业务逻辑中的翻译需求 2.3 满足实时通信的要求 2.4 内容管理系统 2.5 个性化推荐系统 2.6 日志和监控 三、开源类翻译组件解决方案 3.1 国内翻译组件方案汇总 3.1.1 百度翻译 3.1.2 腾讯翻…

Hms?: 1渗透测试

靶机&#xff1a;Hms?: 1 Hms?: 1 ~ VulnHub 攻击机&#xff1a;kail linux 2024 主机扫描阶段发现不了靶机&#xff0c;所以需要按DriftingBlues2一样手动配置网卡 1,将两台虚拟机网络连接都改为NAT模式&#xff0c;并查看靶机的MAC地址 2&#xff0c;攻击机上做主机扫描发现…

linux-环境变量

环境变量是系统提供的一组 name value 的变量&#xff0c;不同的变量有不同的用途&#xff0c;通常都具有全局属性 env 查看环境变量 PATH PATH是一个保存着系统指令路径的一个环境变量&#xff0c;系统提供的指令不需要路径&#xff0c;直接就可以使用就是因为指令的路径…

基于Spring Boot的私房菜定制上门服务系统的设计与实现

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统私房菜定制上门服务系统信息管理难度大&#xff0c;容错率…

qt QMenuBar详解

1、概述 QMenuBar是Qt框架中用于创建菜单栏的类&#xff0c;它继承自QWidget。QMenuBar通常位于QMainWindow对象的标题栏下方&#xff0c;用于组织和管理多个QMenu&#xff08;菜单&#xff09;和QAction&#xff08;动作&#xff09;。菜单栏提供了一个水平排列的容器&#x…