一个老生常谈的问题: 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 节点的替换与更新。

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

相关推荐
天天进步20154 小时前
React Hooks 深入浅出
javascript·react.js·ecmascript
哟哟耶耶4 小时前
react-13react中外部css引入以及style内联样式(动态className与动态style)
前端·css·react.js
qq_400552005 小时前
【React Hooks原理 - useCallback、useMemo】
前端·react.js·前端框架
进取星辰1 天前
20、数据可视化:魔镜报表——React 19 图表集成
前端·react.js·信息可视化
寧笙(Lycode)1 天前
React实现B站评论Demo
前端·react.js·前端框架
莫问alicia2 天前
react + antd 实现后台管理系统
前端·react.js·前端框架·antd
小满zs2 天前
React-router v7 第七章(导航)
javascript·react.js·ecmascript
凉生阿新2 天前
【React】Hooks 解锁外部状态安全订阅 useSyncExternalStore 应用与最佳实践
javascript·安全·react.js
巴巴_羊2 天前
TS typeof运算符
react.js
亦世凡华、2 天前
React--》掌握react构建拖拽交互的技巧
javascript·经验分享·react.js·react-dnd·拖拽实现