react - Fiber架构初步理解
文章目录一、引言useState 的状态丢失谜题1.1 闭包Closure的作用与陷阱⚠️ 闭包陷阱与批处理机制1.2 Fiber 节点的状态存储机制二、React Fiber 架构深度剖析2.1 Fiber 节点的核心数据结构2.2 Fiber 节点的组织形式真正的链表树2.3 Hooks 在 Fiber 中的单向链表排布2.4 为什么 React 选用链表而不是数组2.5 双缓存机制Double Buffering2.6 Fiber 树的 DFS 遍历过程与伪代码三、链表数据结构基础与 JS 实现3.1 什么是单链表3.2 单链表的 JavaScript 实现3.3 链表 vs 数组 性能对比四、总结一、引言useState 的状态丢失谜题在 React 函数组件中每次组件重新渲染组件函数都会从头到尾重新执行一次。但为什么通过useState定义的状态State却能在多次渲染之间持久保存更新后的值而不会被重复初始化呢其背后的底层机制涉及闭包Closure与React Fiber 架构的紧密协作。1.1 闭包Closure的作用与陷阱useState返回的状态值如count和更新函数如setCount通过闭包捕获了当前渲染周期的状态快照。function Counter() { const [count, setCount] useState(0); // 闭包捕获当前渲染周期的 count 值 const handleClick () { // 提交更新闭包中的 count 是当前渲染周期的值例如 0 setCount(count 1); }; return button onClick{handleClick}{count}/button; }⚠️ 闭包陷阱与批处理机制看下面这个常见场景function Counter() { const [count, setCount] useState(0); const handleClick () { setCount(count 1); // 第一次更新基于当前闭包 count 0 setCount(count 1); // 第二次更新仍基于当前闭包 count 0 }; // 点击后 count 只会变为 1而不是 2 return button onClick{handleClick}{count}/button; }原因一闭包在同一次事件回调中两次count拿到的都是旧快照0。原因二批处理与计算传入的是具体数值setCount(1)React 在下一轮合并更新时会将旧值覆盖。解决方案如果需要依赖最新状态需改用函数式更新setCount(prev prev 1)React 会将更新函数放入队列依次计算出最终状态。1.2 Fiber 节点的状态存储机制闭包负责在单次渲染中提供状态快照而真正跨渲染周期存储状态的地方是 React 的 Fiber 节点。每个函数组件在 React 内部都对应一个 Fiber 节点其状态链表存储在 Fiber 节点的memoizedState属性中。组件重新渲染时useState不会重新初始化状态而是直接从 Fiber 节点的memoizedState中读取最新计算好的状态值。// Fiber 节点存储 Hook 状态的结构示意{memoizedState:{memoizedState:42,// 当前 Hook 的状态值queue:{...},// 待处理的 setState 更新队列next:{...}// 指向下一个 Hook}}二、React Fiber 架构深度剖析在 React 16 之前Stack Reconciler渲染过程通过原生的递归函数调用栈完成一旦开始就无法中断容易导致主线程卡顿。引入Fiber 架构后React 将树形结构改造成了指针链表从而实现了可中断、可恢复的增量渲染。2.1 Fiber 节点的核心数据结构每一个 Fiber 节点都是一个 JavaScript 对象包含了组件类型、状态、DOM 节点以及调度所需的链表指针interfaceFiber{// 1. 标识组件/节点类型tag:WorkTag;// 如 FunctionComponent, ClassComponent, HostComponenttype:any;// 对于函数组件type 是组件函数本身对于 DOM 节点是 div 等字符串// 2. 状态与副作用存储memoizedState:any;// 状态存储函数组件的 Hooks 单向链表保存在这里stateNode:any;// 对应的真实 DOM 节点或类组件实例updateQueue:UpdateQueueany|null;// 状态更新队列flags:Flags;// 副作用标记如 Placement 插入、Update 更新、Deletion 删除// 3. 核心指针链表构成 Fiber 树的关键child:Fiber|null;// 指向“第一个子节点”sibling:Fiber|null;// 指向“下一个兄弟节点”return:Fiber|null;// 指向“父节点”// 4. 双缓存机制指针alternate:Fiber|null;// 指向另一棵树current - workInProgress对应的 Fiber 节点}2.2 Fiber 节点的组织形式真正的链表树以如下组件结构为例function App() { return ( div Header / Content / /div ); }传统树形结构是用数组包含子节点而Fiber 树本质上是一棵通过指针串联起来的单链表架构App Fiber | (child) div Fiber | (child) Header Fiber ----(sibling)---- Content Fiber | (return) | (return) ----------------------------------- div Fiber重点父节点的child仅仅指向第一个子节点Header。子节点通过sibling链表指向下一个兄弟节点Content。所有子节点通过return指向其父节点div。2.3 Hooks 在 Fiber 中的单向链表排布函数组件内的多个 Hook如useState,useEffect按调用顺序存在于 Fiber 的memoizedState中以单向链表的形式组织function Example() { const [count, setCount] useState(0); // Hook 1 const [name, setName] useState(); // Hook 2 useEffect(() {}, []); // Hook 3 }对应的 Hook 内存链表Fiber.memoizedState | Hook1 (count) ----next---- Hook2 (name) ----next---- Hook3 (effect) ---- null | | | memoizedState: 0 memoizedState: memoizedState: { ... }为什么 React 规定 Hook 不能写在条件语句或循环中因为 React 内部依靠单向链表的游标指针按 Hook 执行顺序逐个读取节点。如果条件分支导致某个 Hook 被跳过链表结点的匹配就会全部错位2.4 为什么 React 选用链表而不是数组可中断与恢复最核心原因原生的递归调用栈无法暂停。而使用链表指针模拟调用栈后React 可以将当前工作节点记录在一个全局指针如workInProgress中。当浏览器帧率不足通过Scheduler调度时React 随时可以中断Pause渲染让出主线程下一帧再从记录的指针处恢复Resume遍历。高效的结构变更在树的 DOM 节点插入、删除、移动时链表只需要修改指针如child或sibling操作的时间复杂度为O ( 1 ) O(1)O(1)效率极高。保证 Hook 调用的顺序确定性单向链表天生具备严格的前后依赖关系能以极低的开销保障 Hook 调用的固定顺序。2.5 双缓存机制Double Buffering在 2.1 节提到了alternate属性这是 React 优化 DOM 渲染的核心思想——双缓存机制current树代表当前在屏幕上显示的真实 DOM 对应的 Fiber 树。workInProgress树代表正在内存中构建/更新的 Fiber 树。当触发更新时React 会在后台基于current树构建全新的workInProgress树。所有 Diff 比较与副作用标记都在后台完成后React 只需要将指针切换一次直接把workInProgress替换为current即可无缝完成界面更新避免中间状态导致的页面闪烁。2.6 Fiber 树的 DFS 遍历过程与伪代码React 遍历 Fiber 树采用的是深度优先遍历DFS整个过程被称为WorkLoop工作循环// 模拟 Fiber 树单次单元工作的伪代码functionperformUnitOfWork(fiber){// 1. 执行当前 Fiber 节点的 reconcile/更新逻辑 ...// 2. 如果有子节点优先深入向下遍历 childif(fiber.child){returnfiber.child;}// 3. 如果没有子节点说明到了叶子节点查找兄弟节点或向父节点回溯letnextFiberfiber;while(nextFiber){// 完成当前节点的“归complete Work”逻辑 ...// 如果有兄弟节点走 sibling 指针if(nextFiber.sibling){returnnextFiber.sibling;}// 否则回溯给父节点 return 指针继续向上查找nextFibernextFiber.return;}returnnull;// 回溯到 Root 节点遍历结束}三、链表数据结构基础与 JS 实现为了更好地理解 Fiber 的设计我们回顾一下数据结构中的单链表原理。3.1 什么是单链表链表Linked List是一种线性数据结构由一系列节点Node组成。节点在内存中无需连续存储每个节点包含数据域存储节点的值/状态指针域指向下一个节点的引用3.2 单链表的 JavaScript 实现// 1. 定义链表节点结构classListNodeT{value:T;next:ListNodeT|null;constructor(value:T){this.valuevalue;this.nextnull;}}// 2. 创建单链表: 1 - 2 - 3constnode1newListNode(1);constnode2newListNode(2);constnode3newListNode(3);node1.nextnode2;node2.nextnode3;// 3. 遍历单链表letcurrent:ListNodenumber|nullnode1;while(current!null){console.log(current.value);// 依次输出: 1, 2, 3currentcurrent.next;}3.3 链表 vs 数组 性能对比特性数组 (Array)链表 (Linked List)内存分配连续内存空间动态随机分配内存非连续随机访问 (Index)快时间复杂度O ( 1 ) O(1)O(1)慢需要沿指针遍历时间复杂度O ( n ) O(n)O(n)头部/中间插入与删除慢需要移动后续所有元素O ( n ) O(n)O(n)快只需改变对应指针指向O ( 1 ) O(1)O(1)适用场景需要快速检索/遍历元素的场景元素频繁变动、大小不固定、需要随时中断/中断恢复的场景四、总结React 将复杂的 UI 界面更新拆解为高效率的计算调度其核心贯穿逻辑如下useState的存储依赖组件对应的Fiber 节点其内部以单向链表的形式在memoizedState中保存每个 Hook 的状态。闭包与快照组件函数每次渲染通过闭包获取当次渲染的状态快照修改状态时使用函数式更新可以避免闭包拿旧值的问题。Fiber 的链表拓扑Fiber 节点通过child子、sibling兄、return父三条指针将组件树串联为链表结构。可中断渲染的基石正是基于这种显示的指针链表结构React 才能脱离原生 JS 递归栈的限制实现无卡顿的增量渲染Incremental Rendering与双缓存机制。 感谢阅读想了解更多 我的博客网站 | 记录思考分享干货 我的个人主页 | 关于我、开源项目

相关新闻

多场景智能外呼怎么选?外呼机器人落地全科普指南

多场景智能外呼怎么选?外呼机器人落地全科普指南

不少企业管理者在数字化拓客、客户服务过程中,始终被四大难题困扰:人工外呼人力成本居高不下,批量触达效率极低;市面上外呼机器人对话机械生硬,客户接通直接挂断;系统无法对接企业现有客户管理工具&#xf…

2026/8/12 21:28:16 阅读更多 →
Linux systemd 服务管理实战:Unit 文件、开机自启

Linux systemd 服务管理实战:Unit 文件、开机自启

现代 Linux 都用 systemd 管理服务,替代了老的 SysVinit。部署 Java/Python 项目、写开机自启、看服务日志,全都要用 systemd。这篇从 Unit 文件写到实战部署。 一、什么是 systemd systemd 系统和服务管理器老方式 service(SysVinit&#x…

2026/8/12 21:28:16 阅读更多 →
Markdown本地图片预览全攻略:原理、方案与最佳实践

Markdown本地图片预览全攻略:原理、方案与最佳实践

1. 项目概述:为什么我们需要在.md文件中优雅地插入本地图片?如果你经常用Markdown写文档、记笔记,或者维护项目README,肯定遇到过这个烦心事:辛辛苦苦在.md文件里插入了本地图片的路径,结果在编辑器里只看到…

2026/8/12 21:28:16 阅读更多 →

最新新闻

Docker Minecraft Server高效实战:10个进阶优化技巧提升服务器性能

Docker Minecraft Server高效实战:10个进阶优化技巧提升服务器性能

Docker Minecraft Server高效实战:10个进阶优化技巧提升服务器性能 【免费下载链接】docker-minecraft-server Docker image that provides a Minecraft Server for Java Edition that automatically installs/upgrades versions, modloaders, modpacks and more at…

2026/8/12 22:06:41 阅读更多 →
ACM模式输入输出全解析:华为OD机试与算法面试必备技能

ACM模式输入输出全解析:华为OD机试与算法面试必备技能

1. 从“华为OD机试”说起:为什么ACM模式下的输入输出是算法面试的第一道坎?最近和不少准备面试的朋友交流,尤其是那些瞄准了华为OD(Outsourcing Dispatch)或其他大厂技术岗位的朋友,发现一个挺普遍的现象&a…

2026/8/12 22:06:41 阅读更多 →
lua-languages高级技巧:静态类型检查与函数式编程实战

lua-languages高级技巧:静态类型检查与函数式编程实战

lua-languages高级技巧:静态类型检查与函数式编程实战 【免费下载链接】lua-languages Languages that compile to Lua 项目地址: https://gitcode.com/gh_mirrors/lu/lua-languages 在Lua生态中,lua-languages项目集合了多种可编译为Lua的编程语…

2026/8/12 22:06:41 阅读更多 →
OpenClaw Web控制面板安装与使用指南

OpenClaw Web控制面板安装与使用指南

1. OpenClaw控制面板的必要性 第一次接触OpenClaw(Clawdbot)时,我和大多数开发者一样被它的命令行界面劝退。那些复杂的参数组合和晦涩的错误提示,让简单的操作变得异常痛苦。直到发现它的Web控制面板,整个使用体验才发…

2026/8/12 22:06:41 阅读更多 →
Unity-UIGradient终极指南:从原理到实战的完整解析

Unity-UIGradient终极指南:从原理到实战的完整解析

Unity-UIGradient终极指南:从原理到实战的完整解析 【免费下载链接】Unity-UIGradient A UI gradient effect script for Unity 项目地址: https://gitcode.com/gh_mirrors/un/Unity-UIGradient 在Unity游戏开发中,UI视觉效果的重要性不言而喻。传…

2026/8/12 22:06:41 阅读更多 →
20-无人售货柜项目完整版本迭代实战:从开发、测试、灰度、量产全流程

20-无人售货柜项目完整版本迭代实战:从开发、测试、灰度、量产全流程

20-无人售货柜项目完整版本迭代实战:从开发、测试、灰度、量产全流程 前言 大家好,我是黒漂技术佬。 前面 16 篇文章,我们从 Git 分支模型讲到 CI/CD、代码规范、安全审计,几乎把版本管理和 DevOps 的工具链都翻了一遍。这篇是整个…

2026/8/12 22:05:40 阅读更多 →

日新闻

Ubuntu 22.04安装与使用tree命令:高效管理Linux目录结构

Ubuntu 22.04安装与使用tree命令:高效管理Linux目录结构

1. 为什么需要一个“目录树”工具?在Linux世界里,尤其是Ubuntu这样的发行版,命令行是很多人的主战场。我们每天都要和文件、目录打交道。ls命令是查看目录内容的首选,它简洁、高效,能列出文件名、权限、大小等关键信息…

2026/8/12 9:33:34 阅读更多 →
博思AI智能体:意图识别、思考链与性能优化的工程实践

博思AI智能体:意图识别、思考链与性能优化的工程实践

在AI应用从“能用”走向“好用”的进程中,系统的响应速度、决策透明度与高并发稳定性是决定用户体验的关键。博思AI智能体近期完成了一次重要的专项优化,聚焦于意图识别、思考链展示与全链路压测三大核心领域,将系统从功能实现推向了工程卓越…

2026/8/12 9:33:34 阅读更多 →
子代理架构:AI智能体任务分解与协同执行的核心原理与实践

子代理架构:AI智能体任务分解与协同执行的核心原理与实践

1. 项目概述:为什么我们需要“子代理”?最近在折腾各种AI应用和自动化流程时,我越来越频繁地遇到一个瓶颈:单个AI智能体(Agent)的能力边界。无论是处理复杂的多步骤任务,还是需要同时调用多个专…

2026/8/12 9:33:34 阅读更多 →

周新闻

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁 【免费下载链接】baidupankey 在线查询网盘提取码(维护中 rm repo) 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 你是否曾经在深夜寻找一份重要资料&#x…

2026/8/12 1:11:09 阅读更多 →
如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南 【免费下载链接】chinese_license_plate_generator 中国车牌生成器 项目地址: https://gitcode.com/gh_mirrors/ch/chinese_license_plate_generator 中国车牌生成器是一个基于Python的开源项目&#xff0c…

2026/8/12 1:11:09 阅读更多 →
收藏!小白程序员轻松入门大模型,从Harness工程开始实践

收藏!小白程序员轻松入门大模型,从Harness工程开始实践

文章强调学习大模型不应只关注模型本身,而应重视模型外的系统搭建,即Harness。提出AgentModelHarness的实用公式,详细介绍Harness的四个层次:持久化层、执行层、控制层和观察与验证层。文章还探讨了上下文工程、工具设计、AGENTS.…

2026/8/12 1:11:08 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/11 17:09:45 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/12 1:11:10 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/11 17:09:45 阅读更多 →