fiber其实是一个多进程并发的过程。
这里涉及到并发和并行的区别
并发:操作系统按照一定的调度策略,将CPU的执行权分配给多个进程,让多个进程交替执行,由于时间很快,给用户一种同时在执行多种任务的错觉。实际上单核的CPU的物理环境下只能同时执行一个任务。
并行:与并发相反,是多核的物理环境的支持下,才能实现并行,也就是同时执行多个进程。
🔴并行可以是并发,而并发不一定是并行,两种不能划等号, 并行一般需要物理层面的支持
类比js的执行环境
js是单线程的,也就是说一次只能执行一个任务,并且在浏览器环境中,js线程和GUI渲染线程是不能同时执行的,js执行如果占用太多时间,就会阻碍浏览器的渲染,就会造成页面的卡顿、用户交互缓慢等情况。
因此,js存在一个缺陷,就是前面的同步任务会影响后面的其他任务的执行,如果开发者埋下一颗炸弹,对于浏览器和用户的交互体验来说,就会产生极差的影响。
对于框架来说,React选择了一种方式来解决这个问题。
快速响应用户,让用户觉得够快,不能阻塞用户的交互。
类似于CPU中的多进程并发,React用fiber的方式实现了多线程’并发‘。
React怎么做的?
React的协调过程:当需要更新某个状态时,React会对比两个虚拟DOM树,找出变化的节点,然后同步更新DOM,这个过程时同步执行的,不给浏览器一丝喘息的机会。
结果:无法响应用户的交互,页面卡顿效果明显。
同步模式下的 React:
改进后模式下的React:
原理:
1.未改进之前的react的协调过程是通过递归来比对两个虚拟DOM树,然后更新DOM,整个递归的过程不可中断,并且中断后需要重新比对,无法记住当前的执行位置,因此在执行的过程中,无法交出执行的控制权。
2.🔴通过fiber架构改进后的react,是通过链表的形式比对虚拟DOM树,由于链表特殊的结构,可以随时中断执行,然后将执行权交给浏览器,用来响应用户交互和浏览器渲染,从而得到更好的用户体验。
fiber是什么
1.协程
类似于generator,是一种让程序停止,并交出程序控制权的流程控制机制。
const tasks = []
function * run() {
let task
while (task = tasks.shift()) {
// 🔴 判断是否有高优先级事件需要处理, 有的话让出控制权 相当于执行异步任务
if (hasHighPriorityEvent()) {
yield
}
// 处理完高优先级事件后,恢复函数调用栈,继续执行...
execute(task)
}
}
解答:
由于浏览器不存在抢占式调度,只能使用协作式调度,即react向浏览器申请时间片来执行自己的任务单元,浏览器每帧(16ms)可能会执行以下任务:
- 处理用户输入事件
- Javascript执行
- requestAnimation 调用
- 布局 Layout
- 绘制 Paint
当每一帧执行完以上程序后,会在空闲的时候或者react时间片过时之后,将控制权转交给react,react执行任务单元并计算剩余的时间,如果时间不足就交回控制权。
2.一个执行单元
将它视作一个执行单元,每次执行完一个'执行单元', React 就会检查现在还剩多少时间,如果没有时间就将控制权让出去.
React的改造
1.fiber架构
之前的react虚拟DOM的比较可以看作是一个栈调用,中间无法中断,无法异步执行。
现在的react虚拟DOM的比较是模拟栈调用而实现的链表调用,将递归的过程转换成迭代。
以下是表示一个虚拟dom节点的fiber结构:
export type Fiber = {
// Fiber 类型信息
type: any,
// ...
// ⚛️ 链表结构
// 指向父节点,或者render该节点的组件
return: Fiber | null,
// 指向第一个子节点
child: Fiber | null,
// 指向下一个兄弟节点
sibling: Fiber | null,
}
由于链表的特殊性,就算任务中断,下次再获取到执行权的时候,仍然可以继续执行。
2.分为两个阶段
协调
协调就是diff的过程,允许中断,会找出所有变化的节点、属性和删除、新增的节点等,并打上tag。
以下生命周期是在协调阶段执行的
-
constructor -
static getDerivedStateFromProps -
shouldComponentUpdate -
render
提交
也就是将所有打标的虚拟dom进行更新的过程,不可中断。以下生命周期将在这个阶段执行:
-
componentDidMount -
componentDidUpdate -
componentWillUnmount
为什么要区分两个阶段?
更新DOM不可被中断。假如有多个组件需要渲染的时候,render和构造函数可以被分成多个任务,异步执行,直到所有组件都被渲染完 毕。
在dom更新阶段,如果可以中断的话,会存在部分更新的情况,导致更新视图不一致。
所以,协调阶段是可以中断的,但是提交阶段不行。
一些放在componentDidMount中执行的副作用也不会因为中断而被执行两遍。
总结:fiber作为一种时间分片技术,通过与浏览器的合作式调度,很大程度上解决了js单线程造成的react更新时间过长引起的用户体验差的问题,使得react实现了‘实时’响应用户操作的功能。
参考文章:
这可能是最通俗的 React Fiber(时间分片) 打开方式(juejin.cn/post/684490…)