一个老生常谈的问题: React 中 Fiber 的理解以及什么是 Fiber 双缓冲?

前言

hello,各位掘友们已经好久没有更新掘金,今天笔者跟大家一起聊聊一个老生常谈的问题"React中Fiber的理解以及什么是Fiber双缓冲",go go go 出发咯。

什么是Fiber

Fiber英文翻译为纤维,什么是纤维?纤维是指由连续或不连续的细丝组成的物质。乍一看,形如这种结构在数据结构中是不是和链表很像,如果想到了这一层那么恭喜你已经正式入门React Fiber了。

实际上在React中,我们可以从三个维度来理解Fiber

  1. 是一种架构,称之为Fiber架构
  2. 是一种数据类型
  3. 动态的工作单元

Fiber 架构

在 React v16之前,使用的是 Stack Reconciler,因此那个时候的 React 架构被称之为 Stack 架构。从 React v16 开始,重构了整个架构,引入了 Fiber,因此新的架构也被称之为 Fiber 架构,Stack Reconciler 也变成了 Fiber Reconciler。各个FiberNode之间通过链表的形式串联起来:

js 复制代码
function FiberNode(tag, pendingProps, key, mode) {
  // ...

  // 周围的 Fiber Node 通过链表的形式进行关联
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  // ...
}

一种数据类型

Fiber 本质上也是一个对象,是在之前 React 元素基础上的一种升级版本。每个 FiberNode 对象里面会包含 React 元素的类型、周围链接的FiberNode以及 DOM 相关信息:

js 复制代码
function FiberNode(tag, pendingProps, key, mode) {
  // 类型
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null; // 映射真实 DOM

  // ...
}

动态的工作单元

在每个 FiberNode 中,保存了本次更新中该 React 元素变化的数据,还有就是要执行的工作(增、删、更新)以及副作用的信息:

js 复制代码
function FiberNode(tag, pendingProps, key, mode) {
  // ...

  // 副作用相关
  this.flags = NoFlags;
  this.subtreeFlags = NoFlags;
  this.deletions = null;
	// 与调度优先级有关  
  this.lanes = NoLanes;
  this.childLanes = NoLanes;

  // ...
}

Fiber 双缓冲

Fiber 架构中的双缓冲工作原理类似于显卡的工作原理。

显卡分为前缓冲区和后缓冲区。首先,前缓冲区会显示图像,之后,合成的新的图像会被写入到后缓冲区,一旦后缓冲区写入图像完毕,就会前后缓冲区进行一个互换,这种将数据保存在缓冲区再进行互换的技术,就被称之为双缓冲技术。

Fiber 架构同样用到了这个技术,在 Fiber 架构中,同时存在两颗 Fiber Tree,一颗是真实 UI 对应的 Fiber Tree,可以类比为显卡的前缓冲区,另外一颗是在内存中构建的 FiberTree,可以类比为显卡的后缓冲区。

在 React 源码中,很多方法都需要接收两颗 FiberTree:

js 复制代码
function cloneChildFibers(current, workInProgress){
  // ...
}

current 指的就是前缓冲区的 FiberNode,workInProgress 指的就是后缓冲区的 FiberNode。

两个 FiberNode 会通过 alternate 属性相互指向:

js 复制代码
current.alternate = workInProgress;
workInProgress.alternate = current;

接下来我们从首次渲染(mount)和更新(update)这两个阶段来看一下 FiberTree 的形成以及双缓存机制:

mount 阶段

首先最顶层有一个 FiberNode,称之为 FiberRootNode,该 FiberNode 会有一些自己的任务:

  • Current Fiber Tree 与 Wip Fiber Tree 之间的切换
  • 应用中的过期时间
  • 应用的任务调度信息

现在假设有这么一个结构:

js 复制代码
<body>
  <div id="root"></div>
</body>
js 复制代码
function App(){
  const [num, add] = useState(0);
  return (
  	<p onClick={() => add(num + 1)}>{num}</p>
  );
}
const rootElement = document.getElementById("root");
ReactDOM.createRoot(rootElement).render(<App />);

当执行 ReactDOM.createRoot 的时候,会创建如下的结构

此时会有一个 HostRootFiber,FiberRootNode 通过 current 来指向 HostRootFiber。

接下来进入到 mount 流程,该流程会基于每个 React 元素以深度优先的原则依次生成 wip(WorkInProcess) FiberNode,并且每一个 wipFiberNode 会连接起来,如下图所示:

生成的 wip FiberTree 里面的每一个 FiberNode 会和 current FiberTree 里面的 FiberNode进行关联,关联的方式就是通过 alternate。但是目前 currentFiberTree里面只有一个 HostRootFiber,因此就只有这个 HostRootFiber 进行了 alternate 的关联。

当 wip FiberTree生成完毕后,也就意味着 render 阶段完毕了,此时 FiberRootNode就会被传递给 Renderer(渲染器),接下来就是进行渲染工作。渲染工作完毕后,浏览器中就显示了对应的 UI,此时 FiberRootNode.current 就会指向这颗 wip Fiber Tree,曾经的 wip Fiber Tree 它就会变成 current FiberTree,完成了双缓存的工作:

update 阶段

点击 p 元素,会触发更新,这一操作就会开启 update 流程,此时就会生成一颗新的 wip Fiber Tree,流程和之前是一样的

新的 wip Fiber Tree 里面的每一个 FiberNode 和 current Fiber Tree 的每一个 FiberNode 通过 alternate 属性进行关联。

当 wip Fiber Tree 生成完毕后,就会经历和之前一样的流程,FiberRootNode 会被传递给 Renderer 进行渲染,此时宿主环境所渲染出来的真实 UI 对应的就是左边 wip Fiber Tree 所对应的 DOM 结构,FiberRootNode.current 就会指向左边这棵树,右边的树就再次成为了新的 wip Fiber Tree

这个就是 Fiber双缓存的工作原理。

总结

所谓 Fiber 双缓冲树,指的是在内存中构建两颗树,并直接在内存中进行替换的技术。在 React 中使用 Wip Fiber Tree 和 Current Fiber Tree 这两颗树来实现更新的逻辑。Wip Fiber Tree 在内存中完成更新,而 Current Fiber Tree 是最终要渲染的树,两颗树通过 alternate 指针相互指向,这样在下一次渲染的时候,直接复用 Wip Fiber Tree 作为下一次的渲染树,而上一次的渲染树又作为新的 Wip Fiber Tree,这样可以加快 DOM 节点的替换与更新。

以上内容便是本期的全部内容了,如果有所帮助还请点赞、收藏、关注支持一下,谢谢大家。

相关推荐
喜樂的CC7 小时前
[react]Next.js之自适应布局和高清屏幕适配解决方案
javascript·react.js·postcss
旭久8 小时前
react+Tesseract.js实现前端拍照获取/选择文件等文字识别OCR
前端·javascript·react.js
outstanding木槿8 小时前
React中 点击事件写法 的注意(this、箭头函数)
前端·javascript·react.js
涵信10 小时前
2024年React最新高频面试题及核心考点解析,涵盖基础、进阶和新特性,助你高效备战
前端·react.js·前端框架
gaog2zh10 小时前
0501路由-react-仿低代码平台项目
react.js·低代码·路由
JiangJiang12 小时前
🚀 Vue人看React useRef:它不只是替代 ref
javascript·react.js·面试
小钰能吃三碗饭20 小时前
第七篇:【React 实战项目】从零构建企业级应用完全指南
前端·javascript·react.js
三小河20 小时前
纯前端调用deepseek v3模型,流式返回,支持md文本、table、代码等
前端·javascript·react.js
三金同学21 小时前
基于Schema代码片段的导出实践
前端·react.js