news 2026/7/18 4:38:15

react 原理二解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
react 原理二解

一、一些问题

① React 会先构建完整 Element Tree 吗?

不会。但刚开始会执行 <App /> 构建最外层的 Element ,然后接下来的 child 组件是在beginWork中逐层执行,Element 是逐层产生的。

② 两个 alternate 是一个对象吗?

不是。是两个 Fiber 实例。

③ 旧 workInProgress 会销毁吗?

不会。对象会复用,但数据会重置。根据element node 和 current fiber 比如,对旧 workInProgress 进行属性覆盖

④ completeWork 会创建 DOM 吗?

会。但不会把 DOM 插入页面。mount 时会创建离屏 dom 节点树,然后 commit 时直接将该树挂载。updata 时仅进行局部更新

Render 阶段 ├── current Fiber 与新 Element 协调 ├── 构建 workInProgress Fiber 树 ├── 新节点:创建 DOM 实例 ├── 已有节点:复用原 DOM 实例 └── 标记 Placement、Update、Deletion 等操作 Commit 阶段 ├── 删除不再需要的 DOM ├── 插入或移动新增 DOM ├── 更新已有 DOM 的属性和文本 └── 将 finishedWork Fiber 树切换为 current

Update 时,completeWork对已经匹配并复用的 Host Fiber,通常不会重新创建 DOM;它直接复用stateNode中原来的 DOM。只有本轮新挂载的 Host Fiber 才会创建新 DOM

⑤ Commit 才创建 DOM 吗?

不是。Commit 负责的是将 Render 准备好的变更应用到真实 DOM 树,包括插入、删除、移动和更新。

⑥ swap 是什么?

commit 的一个阶段

root.current = finishedWork;

把新的 Fiber 树变成当前树,而不是交换 DOM。

before mutation ↓ mutation:插入、删除、移动、更新真实 DOM ↓ root.current = finishedWork (swap) ↓ layout:执行 useLayoutEffect、componentDidMount / componentDidUpdate ↓ passive:稍后执行 useEffect

⑦ 为什么要先改 DOM,再切换 current ?

因为useLayoutEffect和生命周期需要同时看到:

  • 已经更新后的真实 DOM;

  • 已经切换后的新 Fiber 状态。

8、fiber 内的 stateNode (dom)是否已经有指向关系?

有,假如(update)内部有新增子树,那么会先 appendChild 等先将子树拼接,然后 commit 的时候再找到其父 fiber 的stateNode 进行 appendChild 。已有的 dom 本身也已经有这层关系,但不能在 render 就将拼接的子树接上父 fiber 的 stateNode ,因为这会立即影响到页面。

到了 mutation 阶段,React 处理divFiberPlacement

从 divFiber.return 向上寻找 ↓ 找到 section Fiber ↓ parent = sectionFiber.stateNode ↓ 取得 divFiber.stateNode ↓ parent.appendChild(divDOM)

新增 Fiber 会在 Render 阶段先通过return接入 WIP Fiber 树;新增 Host Fiber 的stateNode保存新 DOM,但不会提前指向或挂入已有父 DOM。若新增节点内部还有新 Host 子树,内部可以在completeWork中先组装;它与已有页面 DOM 的连接,则在 Commit 的 Placement 中通过局部appendChildinsertBefore完成。

completeWork直接通过 DOM API 建立新 DOM 节点之间的真实父子关系,树的顶层 DOM 由对应 Host Fiber 的stateNode引用,后代同时可以通过 DOM 的childNodes和各自 Fiber 的stateNode找到。只有它与已挂载页面 DOM 的最后连接,会推迟到 Commit 阶段。

Mount
所有 Host Fiber 都是新的 ↓ completeWork 从下向上创建 DOM ↓ 新 Host 父节点吸收新 Host 子节点 ↓ 形成离屏 DOM 子树 ↓ Commit 把顶层 DOM 插入根容器
Update:只有属性变化
复用 Fiber alternate ↓ 复用 stateNode 中的旧 DOM ↓ 不重新组装 ↓ Commit 局部更新属性或文本
Update:新增一棵 Host 子树
新增子树内部: completeWork 中提前组装 新增子树与已有页面父 DOM: Commit 中 appendChild / insertBefore

二、流程

1、总的流程

┌──────────────────────────────────────────────────────────┐ │ Trigger / Update │ └──────────────────────────────────────────────────────────┘ root.render(rootElement) setState / dispatch props、context 或 Suspense 变化 │ ▼ 创建并入队 Update │ ▼ 选择更新 Lane │ ▼ markRootUpdated │ ▼ ensureRootIsScheduled │ ▼ ┌──────────────────────────────────────────────────────────┐ │ Prepare │ └──────────────────────────────────────────────────────────┘ renderRootSync / renderRootConcurrent │ ▼ prepareFreshStack │ ▼ createWorkInProgress(root.current, null) │ │ 这里只准备 HostRoot WIP │ 不是提前创建完整 WIP 树 ▼ ┌──────────────────────────────────────────────────────────┐ │ Render Phase │ │ 可中断、可重试、可放弃 │ └──────────────────────────────────────────────────────────┘ performUnitOfWork(WIP Fiber) │ ▼ beginWork(current, WIP) │ ├── HostRoot:处理根更新队列,得到根 Element │ ├── FunctionComponent:执行组件,得到 nextChildren │ ├── ClassComponent:调用 render,得到 nextChildren │ └── HostComponent:读取 props.children │ ▼ reconcileChildren │ │ current.child Fibers │ + │ 本层 nextChildren / Elements │ ↓ │ 匹配 key 和 type │ ↓ │ create / reuse child WIP Fiber │ ↓ │ 标记 Placement、Deletion 等 ▼ 返回 child WIP │ ▼ 继续 beginWork(child) │ ▼ 到达叶子节点 │ ▼ completeWork(leaf) │ ├── 新 Host 节点:创建离屏 DOM ├── 已有 Host 节点:复用 DOM,准备更新 ├── appendAllChildren:组装离屏 DOM 子树 └── bubbleProperties:向上汇总 flags │ ├── 有 sibling │ └── beginWork(sibling) │ └── 无 sibling └── completeWork(parent) │ ▼ HostRoot complete │ ▼ finishedWork │ │ 新 WIP Fiber Tree 已完成 │ root.current 仍然是旧树 ▼ ┌──────────────────────────────────────────────────────────┐ │ Commit Phase │ │ 将 Render 的结果应用到宿主环境 │ └──────────────────────────────────────────────────────────┘ before mutation │ ├── 读取 DOM 修改前状态 └── getSnapshotBeforeUpdate │ ▼ mutation │ ├── Placement:插入 / 移动 DOM ├── Update:更新属性 / 文本 ├── ChildDeletion:删除 DOM、卸载组件 ├── 解绑旧 ref └── 执行部分 effect 清理 │ ▼ root.current = finishedWork │ │ Fiber current 指针切换 │ 不是整棵 DOM Tree 替换 ▼ layout │ ├── useLayoutEffect setup ├── componentDidMount ├── componentDidUpdate └── 挂载新 ref │ ▼ 浏览器获得 Paint 机会 │ ▼ passive effects │ ├── 旧 useEffect cleanup └── 新 useEffect setup

2、一句话流程

Update 负责提出变化 ↓ Schedule 负责决定什么时候计算 ↓ beginWork 负责边产生 Element、边协调、边构建 WIP Fiber ↓ completeWork 负责向上收尾、创建离屏 DOM、汇总 flags ↓ Commit mutation 负责修改已挂载 DOM ↓ root.current = finishedWork 负责切换 Fiber 当前版本 ↓ Layout Effects 读取最新 DOM ↓ Passive Effects 处理非布局型副作用
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/18 4:37:31

个人软件激活码机制:轻量级安全实现方案

1. 个人软件激活码机制实现概述在独立开发或小团队协作中&#xff0c;为软件产品设计一套可靠的激活码机制是保护知识产权的基础手段。不同于企业级解决方案的复杂性&#xff0c;个人开发者需要的是轻量但足够安全的实现方案。我经手过7款商业软件的授权系统开发&#xff0c;总…

作者头像 李华
网站建设 2026/7/18 4:35:28

树莓派Pico远程开关控制:前端与嵌入式开发实战

1. 项目背景与核心需求作为一名前端开发者&#xff0c;第一次接触树莓派Pico这类嵌入式开发板时&#xff0c;最直观的入门项目就是实现远程开关控制。这个项目完美结合了前端熟悉的网络交互和硬件控制的新鲜感&#xff0c;通过网页界面控制物理设备的开关状态。核心需求很明确&…

作者头像 李华
网站建设 2026/7/18 4:35:12

IDM激活脚本技术架构解析:Windows注册表操作自动化实现

IDM激活脚本技术架构解析&#xff1a;Windows注册表操作自动化实现 【免费下载链接】IDM-Activation-Script-ZH IDM激活脚本汉化版 项目地址: https://gitcode.com/gh_mirrors/id/IDM-Activation-Script-ZH IDM激活脚本是一个基于Windows批处理语言的自动化工具&#xf…

作者头像 李华
网站建设 2026/7/18 4:35:10

学生选购苹果电脑指南:MacBook、Mac mini、iMac对比分析

对于学生群体来说&#xff0c;选择一台合适的电脑是学习效率的重要保障。苹果电脑以其稳定的系统体验和出色的性能表现&#xff0c;成为许多学生的首选。但在 MacBook、Mac mini 和 iMac 这三款主流产品之间做出选择并不容易&#xff0c;每款产品都有其独特的定位和适用场景。学…

作者头像 李华
网站建设 2026/7/18 4:34:42

Vue3响应式API:computed、watch与watchEffect深度解析

1. 为什么需要理解Vue3的响应式辅助工具在Vue3开发中&#xff0c;computed、watch和watchEffect是我们每天都会用到的核心API。但很多开发者&#xff08;包括曾经的我&#xff09;在使用时都存在一些误区&#xff1a;要么过度使用watch导致性能问题&#xff0c;要么该用compute…

作者头像 李华
网站建设 2026/7/18 4:33:09

机器人落地的三重枷锁:物理、经济与社会约束解析

1. 这不是科幻失效&#xff0c;而是我们对“机器人”三个字的理解错位了“Where are the Robots that Sci-Fi Movies and Books Promised?”——这句话我第一次在2013年东京国际机器人展的茶水间里听一位日本老工程师念出来&#xff0c;他手里捏着半块没吃完的饭团&#xff0c…

作者头像 李华