通俗易懂地理解React Fiber

2,833 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情

Fiber是在React16+首次引入的,至今已经过了好几年了,那么你有了解过Fiber吗?

Fiber是什么

在了解Fiber是什么之前,首先了解一下在Fiber出现之前,React更新视图的原理:通过setState改变数据从而触发虚拟DOM去进行对比,对比结束后将再进行DOM更新。那么更新就会分成两部分:

  • 数据更新,触发虚拟DOM比较
  • 比较完成后更新真实DOM节点

image.png

当对比少的节点时使用这种方法时比较合理的,但是当我们一次更新有几百个甚至更多组件需要进行对比时,由于Diff是一个同步的方法,在进行对比时,由于JS单线程的原因,导致其他的事件都无法响应
当在这个Diff的过程中如果有有通过Input输入信息,那么他将得不到响应,显然这样体验会非常不好。

针对这样的问题,React 16+中首次引入了React Fiber,就是用于解决渲染复杂组件时产生的卡顿问题。

Fiber是如何解决问题的

Fiber的作用就是将Diff的工作分割成很多个块,并分散到不同中执行。同时也新增了可控制新更新和模块执行优先级的能力。

帧:浏览器帧是根据显示器的刷新率而决定的,比如刷新率为 120Hz,则为120帧/s 每帧的时间就为 1000/120 = 8.3ms

简单来说,Fiber的作用就是对Diff的过程进行拆解,把复杂的Diff拆分成不同的执行块,再分布到不同的帧中执行。

Fiber执行过程

Fiber的结构

React在创建虚拟DOM树的同时也会创建与DOM树相同的一个Fiber树(Fiber Tree),Fiber Tree是由Fiber Node组成。 每个Fiber Node主要由三部分组成: returnchildrensibling

  • return:指向父节点

  • children:指向子节点

  • sibling:指向兄弟节点

fiber_02.png

Fiber Node有上面的三个指针,可以确保Diff在暂停之后可以从最后遍历的地方继续进行Diff。

Fiber执行顺序

Fiber Node关于执行优先级相关的有两个参数:ExpirationTimechildExpirationTime

  • ExpirationTime:到期时间,用于判断该节点是否在下一时间片执行
  • childExpirationTime:快速确定子树中是否有不在等待的变化

Fiber通过Fiber Node的ExpirationTime决定执行的优先级,不同节点的ExpriationTime也可能不同,当当前时间超过ExpirationTime时,此时的Fiber Node需要执行的优先级最高,所以,当过期时间越,执行的优先级就越

ExpirationTime有两种计算的方式:

  • computeInteractiveExpiration: 交互事件的过期时间
  • computeAsyncExpiration: 异步更新的过期时间

Fiber更新

当我们在调用setState时,React会将更新的信息放到Fiber的更新队列中,在等待一定的时间后,再将更新队列的信息合并更新到WorkInProgress Tree中。

image.png

  • WorkInProgress Tree: 在遍历Fiber Node时创建的一颗树,一般情况下结构是与Fiber Node相同的,在更新是用于更新组件,更新完毕后会将数据同步到Fiber Tree中。所以在更新开始是,Fiber Tree与WorkInProgress Tree是结构是相同的。

调度

上文提到,Fiber是在浏览器空闲的时候进行Diff的,而判断浏览器是否空闲则是用浏览器提供的API来实现的,用到的API包括:requestIdleCallbackrequestAnimationFrame

  • requestIdleCallback: 当浏览器空闲时执行回调函数
  • requestAnimationFrame: 浏览器在下次重绘之前调用指定的回调函数

当workInProgress Tree生成完之后,会将整颗树交给浏览器渲染到页面上。在这个渲染流程中又分为两步:调和阶段提交阶段

  • 调和阶段:指的是workInProgress Tree的生成阶段,这个时候的执行是可以被暂停和从暂停的地方重新开始的。
  • 提交阶段:指树已经对比并更新完,交给浏览器去DOM生成的阶段,此阶段是不可中断的。

调度的过程如下:

image.png

workInProgressTree的生成过程

image.png

在介绍ExpirationTime也提到过,通过ExpirationTime来判断优先级,递归生成workInProgressTree。

总结

  • Fiber是React为了解决组件嵌套是Diff时间过长导致造成卡顿而实现的一种数据结构,通过Fiber将Diff分块并分散到不同的时间片执行,从而解决Diff卡顿问题。

  • Fiber通过记录父节点,子节点和兄弟节点来保证中断可恢复的。Fiber Node的执行优先级是通过ExpirationTime决定

  • Fiber调度过程分成调和阶段提交阶段,调和阶段可中断,而提交阶段则不能。

参考

  1. 「React Fiber」详细解析
  2. React
  3. React源码解析
  4. 阿里三面:灵魂拷问——有react fiber,为什么不需要vue fiber呢?
  5. React运行机制