fiber

119 阅读4分钟

mp.weixin.qq.com/s/MngOfrMjC…

react 的 setState 的方式,导致它并不知道哪些组件变了,需要渲染整个 vdom 才行。但是这样计算量又会比较大,会阻塞渲染,导致动画卡顿。

所以 react 后来改造成了 fiber 架构,目标是可打断的计算。

为了这个目标,不能边对比边更新 dom 了,所以把渲染分为了 render 和 commit 两个阶段,render 阶段通过 schedule 调度来进行 reconcile,也就是找到变化的部分,创建 dom,打上增删改的 tag,等全部计算完之后,commit 阶段一次性更新到 dom。

打断之后要找到父节点、兄弟节点,所以 vdom 也被改造成了 fiber 的数据结构,有了 parent、sibling 的信息。

所以 fiber 既指这种链表的数据结构,又指这个 render、commit 的流程。

reconcile 阶段每次处理一个 fiber 节点,处理前会判断下 shouldYield,如果有更高优先级的任务,那就先执行别的。

commit 阶段不用再次遍历 fiber 树,为了优化,react 把有 effectTag 的 fiber 都放到了 effectList 队列中,遍历更新即可。

在dom 操作前,会异步调用 useEffect 的回调函数,异步是因为不能阻塞渲染。

在 dom 操作之后,会同步调用 useLayoutEffect 的回调函数,并且更新 ref。

所以,commit 阶段又分成了 before mutation、mutation、layout 这三个小阶段,就对应上面说的那三部分。

react.docschina.org/docs/reconc…

juejin.cn/post/701026…

juejin.cn/post/708160… 这个有问有答

image.png

juejin.cn/post/684490… 蒋鹏飞

image.png

juejin.cn/post/684490… *****

Fiber 也称协程、或者纤程。

在 React 中,虚拟 DOM 对应的就是 Fiber 树。

为了避免大量,频繁操作DOM,React引入了虚拟DOM,在16版本之后又用fiber树替代了虚拟DOM树。但是当发生更新时,如何高效将老的树替换成新的树,这是个难题,按照一些通用的算法解决方案,即使使用最优的算法,算法的复杂度依然为O(n 3 ),n是元素的数量。

这样的结果不是React想要的,基于这样的背景,React团队有了更大胆的想法,既然替换树的代价这么大,那我选择重新建一棵树,没错,简单粗暴。

栈挺好的,代码量少,递归容易理解, 至少比现在的 React Fiber架构好理解😂, 递归非常适合树这种嵌套数据结构的处理。

只不过这种依赖于调用栈的方式不能随意中断、也很难被恢复, 不利于异步处理。 这种调用栈,不是程序所能控制的, 如果你要恢复递归现场,可能需要从头开始, 恢复到之前的调用栈。

因此首先我们需要对React现有的数据结构进行调整,模拟函数调用栈, 将之前需要递归进行处理的事情分解成增量的执行单元,将递归转换成迭代.

React 目前的做法是使用链表, 每个 VirtualDOM 节点内部现在使用 Fiber表示, 它的结构大概如下:

Fiber = { // Fiber 类型信息 type: any, // ... // ⚛️ 链表结构 // 指向父节点,或者render该节点的组件 return: Fiber | null, // 指向第一个子节点 child: Fiber | null, // 指向下一个兄弟节点 sibling: Fiber | null, }

因为使用了链表结构,即使处理流程被中断了,我们随时可以从上次未处理完的Fiber继续遍历下去。

React Fiber 的思想和协程的概念是契合的: 🔴React 渲染的过程可以被中断,可以将控制权交回浏览器,让位给高优先级的任务,浏览器空闲后再恢复渲染。

Fiber优势

开启 Concurrent Mode(并发模式) 后,我们可以得到以下好处(详见Concurrent Rendering in React):

  • 快速响应用户操作和输入,提升用户交互体验
  • 让动画更加流畅,通过调度,可以让应用保持高帧率
  • 利用好I/O 操作空闲期或者CPU空闲期,进行一些预渲染。 比如离屏(offscreen)不可见的内容,优先级最低,可以让 React 等到CPU空闲时才去渲染这部分内容。这和浏览器的preload等预加载技术差不多。
  • 用Suspense 降低加载状态(load state)的优先级,减少闪屏。 比如数据很快返回时,可以不必显示加载状态,而是直接显示出来,避免闪屏;如果超时没有返回才显式加载状态。

尤雨溪在今年的Vue Conf一个观点让我印象深刻:如果我们可以把更新做得足够快的话,理论上就不需要时间分片了。

时间分片并没有降低整体的工作量,该做的还是要做, 因此React 也在考虑利用CPU空闲或者I/O空闲期间做一些预渲染。所以跟尤雨溪说的一样:React Fiber 本质上是为了解决 React 更新低效率的问题,不要期望 Fiber 能给你现有应用带来质的提升, 如果性能问题是自己造成的,自己的锅还是得自己背.