Fiber 起源
Fiber 架构诞生于 React16,是为了解决 React15 及之前版本的 更新不可中断问题 的。
堆栈协调器 Stack Reconciler
咱们晓得,React
在工作的时候中有一个重要的阶段叫做协调阶段 Reconcile
,在React15
的时候,React
采纳的还是堆栈协调 Stack Reconciler
,之所以把它成为堆栈协调,是因为 React 是应用递归来构建虚构 Dom 树(React 15 的叫法) 的,构建过程中,数据被保留在递归调用栈中。因为递归是同步执行的,所以它一旦执行就只能执行完,不能被中途打断。这导致浏览器在执行代码时,Stack Reconciler 常常因为须要协调十分多的节点而消耗大量工夫,而浏览器的 UI 渲染工作迟迟得不到执行,这会导致浏览器产生肉眼可见的掉帧景象。
Fiber 协调器 Fiber Reconciler
为了解决 Stack Reconciler 的递归调用,不可中断问题,React 团队在 React16 公布时推出了全新的 Fiber 架构,旨在解决老版本的更新不可中断问题。React 团队提出了一种新的模式 **Concurrent Mode**
,一个大的同步工作能够分成许多小的同步工作,在浏览器运作的时候,均匀的把这些小的同步工作塞到每一帧的一小块工夫里执行,这种做法咱们称为 可中断的异步更新。而咱们晓得,在 React15 的时候,同步工作因为架构的限度,是不可切分的,一旦暂停工作只能全副中断。然而权限的 Fiber 架构能够保留更新时的运行状态,以便下次调用时能够持续上次的更新。所以说,Fiber 架构为 Concurrent Mode 的推广打下了根底,这种可中断的更新解决了卡顿掉帧的问题,也带给了用户更好的交互体验。
Fiber 工作流程
在 React15 中咱们晓得有虚构 DOM 树,用来建设和实在 DOM 的映射关系,在 Fiber 架构中咱们把种映射成为 Fiber 树。一棵 Fiber 由一个以后利用根节点 FiberRootNode
和以后组件树根节点 rootFiber
形成,rootFiber 实际上是一个 FiberNode
,它又连贯了由其余 FiberNode 组成的子树。FiberRootNode 通过 current 指针连贯 以后组件树 的rootFiber
。这里咱们用了 以后组件树 这个词,其实是为了引出 Fiber 架构下的双缓存机制。
双缓存机制
咱们在图像处理的时候,往往会经验 渲染画面 - 革除画面 - 从新渲染
画面这个过程,往往革除画面后进行重绘的时候,可能会比拟耗时,这时候用户就会感知到闪屏的景象。如果咱们在内存中进行以后帧画面的构建,构建结束后间接替换之前的画面,省去清屏的步骤,这样就节俭了很多工夫,很大水平上改善了用户体验。
所以在 React 中,咱们也应用了双缓存机制,即零碎中始终存在着两棵 Fiber 树,一棵对应的是以后 DOM 在屏幕上显示的画面,被称作 current
,此时咱们称其为 以后组件树 ,一棵是在内存中进行构建的新的 Fiber 树,被称作workInProgress
,此时咱们称其为 正在构建中的组件树。
Fiber 树示例
在如下的代码所渲染的组件中
function App() {
return (
<div className="App">
<header>
<div>
Hello React
</div>
<section>
Happy Hacking
</section>
</header>
</div>
);
}
一棵残缺的 Fiber
树示例如图所示:
根节点 FiberRootNode
会应用 current
指向以后组件树,以后组件树的根节点 rootFIber
会应用 child
指向子节点,如果存在多个子节点,那么子节点与子节点之间又会应用 sibling
指针连贯。
Fiber 首屏渲染
咱们换一个简略的示例,来更好的了解渲染流程:
function App() {const [num , setNum] = useState(0);
return (<p onClick={()=>setNum(num + 1)}>
{num}
</p>
);
}
在一开始,React 会先建设 FiberRootNode 和 rootFiber 作为初始的 Fiber 树,FiberRootNode 的 current 指针指向 rootFiber,此时 rootFiber 是为空的。而后依据组件树返回的 jsx 对象 (就是 createElement 的返回值对象) 在 render 阶段创立新的 rootFiber,这一步是递归的创立 workInProgress,创立完 workInProgress 后,而后在 commit 阶段把这棵树渲染到页面上,此时批改 current 指针指向 workInProgress,使其成为新的 current 树。这就是 Fiber 的首屏渲染流程。current 和 workInProgress 通过 alternate 相互连贯,咱们前面会讲到为何这么做。
Fiber 树更新
在咱们点击 p 使得页面触发更新后,React 会在内存中从新构建一棵残缺的 Fiber 树,也就是 workInProgress,在构建实现后会间接让 current 指针指向它,而后 render 阶段就会基于这个新的 current 进行渲染。在此过程中咱们能够应用 Diff 算法决定是否复用 current 树中的节点,省去创立节点的流程,进一步放慢渲染过程。
节点复用
后面咱们说过了,在页面更新时,因为 React 的双缓存机制,在渲染页面的时候,会先从内存中构建一棵 Fiber 树,等构建结束后,间接扭转 current 指针的指向替换掉以后的 Fiber 树,达到页面更新的目标。所以在构建 workInProgress 树的时候,咱们实际上还有一棵 current 树,因为大多数更新不过是某个款式的扭转或数据小规模更新,导致 UI 变动不是很大,如果此时咱们还在内存中从新的从无到有渲染一棵残缺的 Fiber 树,是很耗时的,所以咱们能够基于 current 树来复用一些节点创立 workInProgress 树,咱们会应用 Diff 算法 (有趣味的小伙伴能够自行学习) 来决定是否复用节点,要复用的节点就是 current.alternate。
有小伙伴可能当初就要问了,current.alternate 不是一棵残缺的树吗,怎么能够间接复用呢?其实在构建 workInProgress 时,current 也在一直的变动,和 workInProgress 同步挪动。