React 的源码与原理解读(二):Fiber 与 Fiber 链表树
创始人
2025-05-31 09:04:05
0

写在专栏开头(叠甲)

  1. 作者并不是前端技术专家,也只是一名喜欢学习新东西的前端技术小白,想要学习源码只是为了应付急转直下的前端行情和找工作的需要,这篇专栏是作者学习的过程中自己的思考和体会,也有很多参考其他教程的部分,如果存在错误或者问题,欢迎向作者指出,作者保证内容 100% 正确,请不要将本专栏作为参考答案。

  2. 本专栏的阅读需要你具有一定的 React 基础、 JavaScript 基础和前端工程化的基础,作者并不会讲解很多基础的知识点,例如:babel 是什么,jsx 的语法是什么,需要时请自行查阅相关资料。

  3. 本专栏很多部分参考了大量其他教程,若有雷同,那是作者抄袭他们的,所以本教程完全开源,你可以当成作者对各类教程进行了整合、总结并加入了自己的理解。

本一节的内容

本节的内容我们将讲述React中的一个很重要的数据结构——Fiber ,本节先着重说明什么是 Fiber 结构,它的数据结构是什么,以及 React 为什么要在 16.X 版本后引入 Fiber 结构,之后的章节会讲述从 React Element 到 Fiber 树的过程以及 Fiber 树的生成和更新

Fiber 结构

Fiber 是 React 16.x 新增的一个数据结构,它由对应的 React Element 生成,它的作用我们会在后面讲到,我们先来看它的定义。它在源码的这个位置:https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactInternalTypes.js

export type Fiber = {tag: WorkTag,key: null | string,elementType: any,type: any,stateNode: any,return: Fiber | null,child: Fiber | null,sibling: Fiber | null,index: number,ref:| null| (((handle: mixed) => void) & {_stringRef: ?string, ...})| RefObject,refCleanup: null | (() => void),pendingProps: any, memoizedProps: any, updateQueue: mixed,memoizedState: any,dependencies: Dependencies | null,mode: TypeOfMode,flags: Flags,subtreeFlags: Flags,deletions: Array | null,nextEffect: Fiber | null,firstEffect: Fiber | null,lastEffect: Fiber | null,lanes: Lanes,childLanes: Lanes,alternate: Fiber | null,actualDuration?: number,actualStartTime?: number,selfBaseDuration?: number,treeBaseDuration?: number,_debugSource?: Source | null,_debugOwner?: Fiber | null,_debugIsCurrentlyTiming?: boolean,_debugNeedsRemount?: boolean,_debugHookTypes?: Array | null,
};

它的数据结构实在过于庞大了,我们拆分成及部分来理解它我们只介绍一些重点的属性,其他的属性想要了解的可以查看源码的注释信息或者查阅其他的资料:

首先是和 DOM相关的属性,它和对应的 React Element 息息相关

tag: WorkTag,        //节点的类型
key: null | string,  //React Element 的 Key 
elementType: any,    //React Element 的 type ,原生标签,类组件或者函数组件
type: any,           //类组件或者函数组件
stateNode: any,      //stateNode 用于记录当前 fiber 所对应的真实 dom 节点或者当前虚拟组件的实例,用于实现对于对DOM的追踪

之后是和整个结构相关的属性,在 React 的 Fiber 架构中,所有的 Fiber 元素将被串联在一起形成一颗双向的链表树,由以下的几个属性来实现

return: Fiber | null,   //表述一个元素的父亲
child: Fiber | null,    //表述一个元素的第一个孩子
sibling: Fiber | null,  //表述一个元素的兄弟
index: number,          //在兄弟节点中的位置

可以用一张图形象的概括它:
请添加图片描述

之后是用于计算 state 和 props 的部分,这是一个 React 组件很重要的组成部分,不过这篇教程主要以讲解 Fiber 结构为主要目的,所以这部分我们会在后续进行讲解,这里按下不表:

pendingProps: any,       // 本次渲染需要使用的 props
memoizedProps: any,      // 缓存的上次渲染使用的 props
updateQueue: mixed,      // 用于状态更新、回调函数、DOM更新的队列
memoizedState: any,      /// 缓存的上次渲染后的 state 
dependencies: Dependencies | null,     // contexts、events 等依赖

之后是副作用相关的部分,也就是说,当我们修改了节点的一些属性,比如 state 或者 props 等的时候,我们的 DOM 可能会发生变化,同时这种变化可能还会影响到孩子节点,具体的流程将会在对 React diff 算法的讲解的时候再次深入,这里我们只是先介绍一下相关的逻辑:

在 render 阶段时,react 会采用深度优先遍历,对 fiber 树进行遍历,Fiber 中会用 flags 表示对当前元素的处理,比如是更新或者删除等等,具体可以查看源码的 Flags 枚举,同时 subtreeFlags 用于表示对孩子节点的处理,而 deletions 则表示我们需要删除的子 Fiber 的序列:

flags: Flags,                       //对当前节点的处理
subtreeFlags: Flags,                //对孩子节点的处理
deletions: Array | null,     //要删除的子节点列表

同时,这个阶段会把每一个有副作用的 fiber 筛选出来,放在一个链表中,以下的三个属性就是来标识这个链表的,这个链表将会在之后的阶段用于更新我们的 DOM

nextEffect: Fiber | null,     //副作用的下一个 Fiber
firstEffect: Fiber | null,    //第一个有副作用的 Fiber
lastEffect: Fiber | null,     //最后一个有副作用的 Fiber

lanes 是用于表示执行 fiber 任务的优先级的,这个将会在后续的文章中详细的讲解

alternate 是用于双缓冲树这个结构,简单来说就是:

  • react 根据双缓冲机制维护了两个fiber树,一颗用于渲染页面 (current),一颗是 workInProgress Fiber 树,用于在内存中构建,然后方便在构建完成后直接昔换 current Fiber树
  • workInprogress Fiber 树的 alternate 指向 Current Fiber树的对应节点, current 表示页面正在使用的 fiber 树
  • 当 workInprogress Fiber 树构建完成,workInProgress Fiber 则成为了 current 渲染到页面上,而之前的 current 则缓存起来成为下一次的workInProgress Fiber,完成双缓冲模型

双缓存模型的优势就是提升效率,可以防止只用一颗树更新状态的丢失的情况,又加快了 dom 节点的替换与更新,后续我们还会详细聊聊这个结构。

为什么要使用 Fiber

在 React 15.x 版本以及之前的版本,Reconciliation 算法采用了栈调和器,它最大的缺点是:当我们开始一个任务的时候,一切都是同步进行的,一旦开始执行就不会中断,直到所有的工作流程全部结束为止。当一个页面比较复杂的时候,状态变更时,组件树层层递归开始更新,js 主线程就不得不停止其他工作开始进行渲染的逻辑,用户的点击输入等交互事件、页面动画等都不会得到响应,体验就会非常的差。下面是官方的一副漫画来讲解这个过程:函数堆栈的调用就像下图一样,层级很深
请添加图片描述

而从刚刚的数据结构中,我们看到,React 将每个节点都封装到了一个 Fiber 中,整个 DOM 树的渲染任务被分成了一个一个小片。当需要进行渲染的时候,React 会从根节点开始一个一个去更新每一个 Fiber ,每当处理完一个 Fiber ,在处理下一个 Fiber 之前,js 可以转而去处理优先级更高、更需要快速响应的任务,而这个优先级被放置在 lanes 这一位中。

React 可以通过优先级来判定是不是中断 Fiber 的处理,调度所有任务的执行,在这样的架构下,虽然任务总的处理时间不变,但是一些需要快速响应的操作可以得到抢占式的响应,类似于操作系统中对线程的抢占式调度,非常强大。对于用户来说,就不会出现因为页面非常复杂,导致渲染任务耗时很长而一致得不到响应的卡顿感受了,官方把它用如下的漫画来表述:

波谷表示执行分片任务,波峰表示执行其他高优先级任务,分片任务在执行结束后释放 js 主线程,高优先级任务可以抢占它,然后继续执行分片任务

请添加图片描述

总结

这节主要讲述了 React 另一个很重要的数据结构 Fiber 结构,我们现在知道了,一段 Jsx 在 React 里会经过这样的流程:首先被解析成 React Element,再从 React Element 被封装为一个 Fiber ,Fiber 由节点之间的层级关系生成一棵 Fiber 的链表树。

而使用 Fiber 的最重要的原因是,通过 Fiber 可以把一个任务分片,因为 js 的单线程的,如果不分片,任务将会长时间占用 js 主线程,导致用户的请求长时间得不到响应,体验极差。通过 Fiber,高优先级的任务可以在一个分片执行完后抢占 js 主线程,从而提升用户的体验。

那么通过 Fiber 的数据结构,我们得出了一些新的问题:

  1. React 的任务是怎么样调度的 —— 这里会用到我们提到过的 lanes
  2. Fiber 树是怎么样生成和更新的 —— 这里会用到我们提到过的双缓冲树和 alternate
  3. React Element 到 Fiber 的过程发生了什么 —— 这里会处理我们提到过的 state 和 props 的部分

那么在之后的章节里,我们将依次来解决这些问题

相关内容

热门资讯

去四川旅游攻略当地团6日游一个... 深入四川的奇幻之旅 四川旅游推荐!当地导游-乐乐:185 8335 5758(加他微信-立减200)...
四川九寨沟峨眉山旅游参团6日游... 亲临仙境,探索四川的奇妙之旅 四川旅游推荐!当地导游-乐乐:185 8335 5758(加他微信-立...
【Linux】基础IO(一) ... 🍎作者:阿润菜菜 📖专栏:Linux系统...
从tomcat源码分析它的启动... 首先我们需要了解两个重要的知识点:一个是tomcat容器的总体结构, 一...
盘点:50%的人都认识的污水处... 污水处理是指被污染的水体经过净化,让其能够再次利用的过程。尽管地球表面2/3被水覆盖&...
Urban Radiance ... Urban Radiance Fields:城市辐射场 摘要:这项工作的目标是根据扫描...
原创 推... “从手到口,从口到心,中国人延续着对世界和人生特有的感知方式。只要点起炉火,端起碗筷,每个平凡的人,...
古贝春展位成德州新青年音乐节“... 大众网记者 张晓琳 徐瑞 德州报道 鼓点震动着夏夜热浪,数万名高举双臂的年轻人在音浪中舞动,古贝春的...
最优化算法 - 动态规划算法 动态规划算法简介 动态规划(Dynamic programming)是一...
璀璨烟花映山海——近万名游客赴... 当端午的艾草清香萦绕未散,六一的欢快童声已在浪尖跳跃。5月31日晚上,一场浪漫的烟花秀在海陵岛大角湾...
海陵岛:端午节偶遇“六一”节 ... 今年的端午佳节与“六一”儿童节不期而遇,传统文化与童趣时光奇妙邂逅,为端午假期旅游市场注入别样的活力...
Linux之进程间通信 目录 进程间通信介绍 一、为什么要进行进程间通信? 二、进程间通信目的 三、进程间通信...
C语言简单工厂模式和工程创建 一,设计模式概念引入① 什么是设计模式设计模式通常被面向对象的软件开发人员所采用&#x...
什么是软件测试?5分钟带你快速... 经常有人问我,你的公司是做什么的?我回答“软件测试”,看着...
Spring 远程加载配置 本文以携程的Apollo和阿里的Nacos为例。 pom中引入一下依赖: ...
四川安居端午游园会启幕 首日迎... 张勤宝 封面新闻记者 刘虎端午佳节,四川省遂宁市安居区节日氛围浓,人们体验端午民俗,感悟传统文化。在...
前端-http请求 目录:  (1)表单  (2)...
到四川旅游团建五天一般多少钱,... 标题:到四川旅游团建五天一般多少钱,驴友亲测!——与乐乐一起的难忘旅程 四川旅游推荐!当地导游-乐乐...
去四川旅游攻略小包团五日游报价... 标题:去四川旅游攻略小包团五日游报价亲测,跟着乐乐玩转天府之国! 四川旅游推荐!当地导游-乐乐:18...