React Fiber基本工作原理

啥是React Fiber?

React Fiber,简单来说就是一个从React v16开始引入的新协调引擎,用来实现Virtual DOM的增量渲染。

说人话:就是一种能让React视图更新过程变得更加流畅顺滑的处理手法。

我们都知道:进程大,线程小。而Fiber(纤维)是一种比线程还要细粒度的处理机制。从这个单词也可以猜测:React Fiber会很“细”。到底怎么个细法,我们接着往下看。

为什么会有React Fiber?

之前说了,React Fiber是为了让React的视图更新过程变得更加流畅顺滑。怎么,之前React的视图更新不流畅,不顺滑了?

还真是的,在React v16之前,React的视图更新确实存在很大的性能问题,其中首当其冲的,就是它的同步更新机制

在React决定要加载或更新一颗组件树之前,会大致做出如下一系列动作:调用各组件的生命周期函数 --> 计算和对比Virtual DOM --> 更新真实的DOM树。这个过程是同步的,也就是说,一旦这个过程开始,它就会一鼓作气跑完,一直到真实DOM树更新完毕。

然而,当组件树比较庞大时,这种机制的问题就来了:一颗拥有300个组件的组件树需要全部更新,假设一个组件更新只需耗时1ms,整棵树更新一次就需要耗时300ms。在这300ms期间,浏览器的主线程一直在“专心致志”地忙着更新这颗组件树(这时函数的调用栈会非常长),对于页面上的任何操作都是“不闻不问”的。在这期间,假如用户在一个输入框敲了几个字,页面上也不会有任何反应,因为渲染按键输入结果也需要主线程来做,然而此时主线程正忙着更新组件树呢。等到300ms结束了,浏览器主线程有空了,才把刚刚敲的那几个字渲染到input输入框内。

太卡了,真的。

由于JavaScript的单线程工作特点,业内一直有个这样的原则:任何动作都不要长时间霸占主线程,如果迟迟不归还主线程,那么在这期间程序就没法对其他输入作出响应。输入了却没有响应,或者说响应来的很慢,也就是我们常常说的“卡顿”。显然,React的同步更新机制在组件树庞大时就违反了这一原则,犯了大忌。

这就是React Fiber出现的原因:为了解决旧版React视图更新的性能瓶颈

React Fiber到底怎么工作的?

首先,React Fiber并没有解决更新庞大组件树耗时长的问题,实际上总的耗时还是一样的长。但是它解决了一个被广大开发者口诛笔伐的恶行:长时间霸占主线程不放。

而它解决的方法就是:分片

它的工作原理是这样的:把耗时长的更新任务拆解成一个个小的任务分片,每执行完一个小的任务分片,都归还一次主线程,看看有没有什么其他紧急任务要做。如果在归还主线程时恰巧发现有紧急任务,那么会马上停掉当前更新任务,转而让主线程去做紧急任务,等主线程做完紧急任务,再重新做更新任务。(注意⚠️:是重新!不是从上次被打断的点继续);如果没有紧急任务,才敢唯唯诺诺地继续做接下来的任务分片。

简单来说,就是降了视图更新的优先级,把更新过程碎片化。

现在我们捋一捋,React Fiber会这样处理一个更新过程:

  1. 将一个更新过程分为Reconciliation阶段和Commit阶段,Reconciliation阶段(调度阶段)会更新数据并生成新的虚拟DOM,并且对新旧的虚拟DOM进行diff,得到需要更新的元素,放到新的更新队列中;而Commit阶段(渲染阶段)则会遍历更新队列,并且将所有的变更一次性更新到真实DOM中;
  2. 对于Reconciliation阶段进行分片处理,这个阶段可以被更紧急的任务打断,分片任务做到一半可能要重来;
  3. 对于Commit阶段,直接一鼓作气把DOM更新完,不能被打断。

React Fiber的实现原理

React Fiber实现的难点有两个:暂停/重启如何实现?任务如何分散执行?

对于前者,暂停/重启意味着我们需要保存状态,这里在实现上用到了具有链表和指针的“单链表树遍历算法”,能够记录遍历过程中的上一步和下一步。

而对于后者,则用到了requestAnimationFramerequestIdelCallback这两个API,其中requestAnimationFrame是浏览器在每一帧都一定会执行的,可以放一些高优先级的任务;而requestIdelCallback则是浏览器在一帧中最后如果有空闲时间才会去执行,可以放一些低优先级的任务,需要polyfill(因为兼容性不佳)。

React Fiber对我们日常开发有什么影响?

React Fiber在Reconciliation阶段可能会调用以下生命周期函数(这也意味着在这个阶段的生命周期函数在一次加载和更新过程中可能会被多次调用):

  • componentWillMount
  • componentWillUpdate
  • componentWillReceiveProps
  • shouldComponentUpdate

如果你恰巧没有上react hooks的车,而是使用传统的类组件进行开发,那么切记,不要在以上几个生命周期函数中做只需要做一次的操作(比如:页面初始化时发起一个ajax请求获取数据)。

如果你平常使用react hooks进行开发,那没事了,就当看了个热闹。

posted @ 2021-02-18 16:01  陌上兮月  阅读(1460)  评论(0编辑  收藏  举报