跳到主内容

理解React fiber的思想架构

· 5分钟阅读

React 每一次大的版本更新都来带来一些新的概念或者专有名词,在 React16 之后带来了很多新的特性,例如 hooks,ErrorBoundary,新的 Context 实现方案,fiber 等等。本文主要针对 react fiber 做一个简单的介绍

为什么需要 react fiber

react fiber 是 react 对 react 15 重构过程中产出的一种新的概念,那么现在思考一下为什么要重构?是旧版本有什么缺点呢?还是为了完成新的 KPI???🤡

React 重构的背景

我们都知道 React 离不开虚拟 DOM,在状态变更时需要构建新的虚拟 DOM 树并进行新旧 DOM DIFF 计算,这一个过程叫做 reconcilation(协调)过程,然而 React 旧的版本依赖于栈结构,所以整个过程叫做 stack reconciliation(栈协调)。栈的一个明显的缺点就是无法中断,暂停和恢复。每次状态变更开始同步递归遍历组件树构建虚拟 DOM 树=>根据 DIFF 算法找出差异=>Patch 差异,整个过程无法中断,一旦组件树过于庞大,整个过程如果耗时大于 16ms,那么就会阻塞浏览器渲染和用户交互等操作,导致卡顿现象,影响用户体验。

双向链表

既然栈这种数据结构无法中断,暂停和恢复,React 想出了一种新的数据结构来代替栈,那就是双向链表。现在 React 描述组件树的结构如下

一个 Fiber Node 的定义如下

https://github.com/facebook/react/blob/3dc41d8a2590768a6ac906cd1f4c11ca00417eee/packages/react-reconciler/src/ReactFiber.new.js#L114-L191

FiberNode 的部分属性含义,return 代表父节点,child 代表子节点 ,sibling 代表兄弟节点,还有一些存储新旧 props 的 memorizedPropspendingProps,状态存储的 memoizedState

比如现在有这样一个组件

const App = () => {
return (
<div className="container">
<a href="1024nav.com">1024nav</a>
<h1>前端面经</h1>
</div>
);
};

生成的 Fiber Tree 结点如下

fiber 如何工作

创建 fiber node 结点

创建 fiber node 通过 createFiberFromTypeAndProps 方法判断 fiberTag 的类型,fiberTag 的定义可以点击查看

https://github.com/facebook/react/blob/3dc41d8a2590768a6ac906cd1f4c11ca00417eee/packages/react-reconciler/src/ReactFiber.new.js#L464-L471

然后通过 createFiber 方法来创建节点

https://github.com/facebook/react/blob/3dc41d8a2590768a6ac906cd1f4c11ca00417eee/packages/react-reconciler/src/ReactFiber.new.js#L577-L580

requestIdleCallback 实现任务调度

requestIdleCallback 可以在浏览器空闲的时候被调用,可以让开发人员在主事件循环执行低优先级任务,从而不影响浏览器的动画和用户输入等高优先级的任务。发生的时机是下一次通过事件循环之前

react 通过自己实现的 requestIdleCallback 来实现任务的调度,具体代码可以下面查阅

https://github.com/facebook/react/blob/eeb817785c771362416fd87ea7d2a1a32dde9842/packages/scheduler/src/Scheduler.js#L212-L222

总结

  • react 使用了新的 fiber 架构,但是底层的核心 diff 算法还是保持与旧的一致,比如不同的组件类型同样是销毁重新创建,对于列表渲染仍然需要 key 来标记优化等

  • fiber 的对任务排了优先级,对 UI 交互和动画渲染等任务优先执行,对 DOM diff 计算等任务延后执行

  • 每个 fiber 代表每一个工作单元,react 通过重新实现了一个虚拟栈桢,可以方便任务调度,每一个工作单元可以中断,暂停和重启,这也是 react 新版本对生命周期做了调整的原因

  • 通过自定义 requestIdleCallback 方法,可以在浏览器空闲的时候执行低优先级的任务