前言
从 React 16 开始,React 采用了 Fiber 机制替代了原先基于原生执行栈递归遍历 VDOM 的方案,提高了页面渲染性能和用户体验。乍一听 Fiber 好像挺神秘,在原生执行栈都还没搞懂的情况下,又整出个 Fiber,还能不能愉快的写代码了。别慌,老铁!下面就来唠唠关于 Fiber 那点事儿。
什么是 Fiber
Fiber 的英文含义是“纤维”,它是比线程(Thread)更细的线,比线程(Thread)控制得更精密的执行模型。在广义计算机科学概念中,Fiber 又是一种协作的(Cooperative)编程模型,帮助开发者用一种【既模块化又协作化】的方式来编排代码。
![7aa5dbd6665b91a50992445f5188f677.png](https://img-blog.csdnimg.cn/img_convert/7aa5dbd6665b91a50992445f5188f677.png)
简单点说,Fiber 就是 React 16 实现的一套新的更新机制,让 React 的更新过程变得可控,避免了之前一竿子递归到底影响性能的做法。
关于 Fiber 你需要知道的基础知识
1 浏览器刷新率(帧)
页面的内容都是一帧一帧绘制出来的,浏览器刷新率代表浏览器一秒绘制多少帧。目前浏览器大多是 60Hz(60帧/s),每一帧耗时也就是在 16ms 左右。原则上说 1s 内绘制的帧数也多,画面表现就也细腻。那么在这一帧的(16ms) 过程中浏览器又干了啥呢?
![0950183f12c9226a7b41b98f68feb9f5.png](https://img-blog.csdnimg.cn/img_convert/0950183f12c9226a7b41b98f68feb9f5.png)
通过上面这张图可以清楚的知道,浏览器一帧会经过下面这几个过程:
- 接受输入事件
- 执行事件回调
- 开始一帧
- 执行 RAF (RequestAnimationFrame)
- 页面布局,样式计算
- 渲染
- 执行 RIC (RequestIdelCallback)
第七步的 RIC 事件不是每一帧结束都会执行,只有在一帧的 16ms 中做完了前面 6 件事儿且还有剩余时间,才会执行。这里提一下,如果一帧执行结束后还有时间执行 RIC 事件,那么下一帧需要在事件执行结束才能继续渲染,所以 RIC 执行不要超过 30ms,如果长时间不将控制权交还给浏览器,会影响下一帧的渲染,导致页面出现卡顿和事件响应不及时。
2. JS 原生执行栈
React Fiber 出现之前,React 通过原生执行栈递归遍历 VDOM。当浏览器引擎第一次遇到 JS 代码时,会产生一个全局执行上下文并将其压入执行栈,接下来每遇到一个函数调用,又会往栈中压入一个新的上下文。比如:
function A(){
B();
C();
}
function B(){}
function C(){}
A();
引擎在执行的时候,会形成如下这样的执行栈:
![aca097dc4584053869f173253dc612a1.png](https://img-blog.csdnimg.cn/img_convert/aca097dc4584053869f173253dc612a1.png)
浏览器引擎会从执行栈的顶端开始执行,执行完毕就弹出当前执行上下文,开始执行下一个函数,直到执行栈被清空才会停止。然后将执行权交还给浏览器。由于 React 将页面视图视作一个个函数执行的结果。每一个页面往往由多个视图组成,这就意味着多个函数的调用。
如果一个页面足够复杂,形成的函数调用栈就会很深。每一次更新,执行栈需要一次性执行完成,中途不能干其他的事儿,只能"一心一意"。结合前面提到的浏览器刷新率,JS 一直执行,浏览器得不到控制权,就不能及时开始下一帧的绘制。如果这个时间超过 16ms,当页面有动画效果需求时,动画因为浏览器不能及时绘制下一帧,这时动画就会出现卡顿。不仅如此,因为事件响应代码是在每一帧开始的时候执行,如果不能及时绘制下一帧,事件响应也会延迟。
3. 时间分片(Time Slicing)
时间分片指的是一种将多个粒度小的任务放入一个时间切片(一帧)中执行的一种方案,在 React Fiber 中就是将多个任务放在了一个时间片中去执行。
4. 链表
在 React Fiber 中用链表遍历的方式替代了 React 16 之前的栈递归方案。在 React 16 中使用了大量的链表。例如:
- 使用多向链表的形式替代了原来的树结构
例如下面这个组件:
<div id="id">
A1
<div id="B1">
B1
<div id="C1"></div>
</div>
<div id="B2">
B2
</div>
</div>
会使用下面这样的链表表示:
![ae58bf33ad4c56acd32b78ad4d062a97.png](https://img-blog.csdnimg.cn/img_convert/ae58bf33ad4c56acd32b78ad4d062a97.png)
</