谈谈Fiber架构
fiber之前是怎么样的?
-
React会递归对比虚拟DOM,找出变动的节点,然后同步更新这些节点
-
这个过程中,React会一直占用着浏览器资源,
- 1、会导致用户触发事件得不到响应
- 2、导致掉帧,用户可能会感觉到卡顿
-
遍历递归过程中,执行栈会越来越深,就会很卡,而且不能中断,因为中断了,恢复就很难了
-
递归太消耗性能
-
为什么需要fiber?
-
支持任务的可中断,为了使react渲染的过程可以被中断 【fiber可以打断diff算法】
- 可以将控制权交给浏览器
- 合理分配CPU资源,从而提高用户的响应速度
-
fiber可以优先处理优先级高的任务
-
对于计算量比较大的计算或者DOM的计算渲染,就不会显得卡顿
什么是fiber?
React Fiber是React核心算法的重新实现
最主要特点就是:使 Virtual DOM 可以进行增量式渲染,可以把一个大的渲染任务分为很多小阶段,按照优先级来执行这些任务分片,优先级高的会先执行
-
fiber是一个js是对象
-
React Fiber是React核心算法的重新实现
-
主要特点:使 Virtual DOM 可以进行增量式渲染,能够将渲染工作分割成多块,并将其分散到多个帧上
-
fiber也就是时间切片,合理分配CPU资源,从而提高用户的响应速度
-
时间切片:将长任务分割为一个个执行时间很短的任务
-
React Fiber把更新过程碎片化,每执行完一段更新过程,就把控制权交还给React负责任务协调的模块,看看有没有其他紧急任务要做,如果没有就继续去更新,如果有紧急任务,那就去做紧急任务。
- fiber可以优先处理优先级高的任务
-
维护每一个分片的数据结构,就是Fiber
-
React Fiber会找出需要更新哪些DOM ,这个阶段【diif算法阶段】是可以被打断的
- fiber可以打断diff算法
-
React通过Fiber将树的遍历变成了链表的遍历
-
链表的优点: (链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据,另一个是存储指向下一个结点地址的指针)
- 插入(添加)和删除不需要移动其他元素,会节省时方便更新的时候暂停、终止,我们可以随时停止遍历并稍后恢复。
-
恢复的话:就是重新进入循环,不过需要在循环外保存上次退出时的状态
-
为什么时间切片不使用 generator?
主要是2个原因:
1、generator 必须将每个函数都包装在 Generator 堆栈中。这不仅增加了很多语法开销,而且还增加了现有实现中的运行时开销。虽然有胜于无,但是性能问题仍然存在。
2、最大的原因是生成器是有状态的。无法在其中途恢复。如果你要恢复递归现场,可能需要从头开始, 恢复到之前的调用栈。
如何判断当前是否有优先级高任务?
- 只能约定一个合理的执行时间,超过了这个执行时间,如果任务还没有执行,中断当前任务,将控制权交还给浏览器
requestIdleCallback:
利用requestIdleCallback的API可以让浏览器的空闲时段内执行回调排队
- 这个回调会传入一个参数,可以指定timeout超时时间,如果超过这个timeout时间还没有执行,那么就会把这个回调放入事件循环里去排队执行
- requestIdleCallback兼容性很差,react为了兼容,自己实现requestIdleCallback
React的五个优先级等级
- Immediate (立刻): 最高优先级,这个优先级需要马上执行,不能被中断
- UserBlocking(用户操作有关): 用户交互的结果,需要及时得到反馈给用户,第二优先级
- Normal :不需要用户立即就能感受到的变化,比如网络请求 (普通优先级)
- Low: 任务延后执行 ,但是需要执行(低优先级)
- Idle: 可以无限延后执行,就算不执行,也不影响
如何判断浏览器有空闲时间?
- 需要知道浏览器在一帧内要做什么事情
js的执行、don构建、render布局,绘制
- 在一帧里浏览器做完本质工作之后,剩下时间就是有空闲的时间
如果浏览器一直没有空闲怎么办?
- 使用requestIdleCallback设置超时时间timeout, 超过超时时间,就把回调会放到事件循环里排队执行
React的五个优先级等级
- Immediate (立刻): 最高优先级,这个优先级需要马上执行,不能被中断
- UserBlocking(用户操作有关): 用户交互的结果,需要及时得到反馈给用户,第二优先级
- Normal :不需要用户立即就能感受到的变化,比如网络请求 (普通优先级)
- Low: 任务延后执行 ,但是需要执行(低优先级)
- Idle: 可以无限延后执行,就算不执行,也不影响
Fiber 架构是如何来管理状态的更新
主要用于实现可中断和优先级管理的渲染
- 当组件需要更新的时候,React 并不会立即开始执行更新操作,fiber 会将更新操作拆分为一系列的小任务,称为 Fiber 节点,这些节点形成一个称为 Fiber 树的结构,React 会根据优先级决定哪些 Fiber 节点需要被执行,而且可以在需要时暂停和恢复执行
- 每个 Fiber 节点都会被赋予一个优先级,用于决定该节点何时被执行
Fiber 实现原理
实现的方式是requestIdleCallback这一 API,但requestIdleCallback兼容性很差,react为了兼容,自己实现requestIdleCallback
Fiber数据结构
一个 fiber 就是一个 JavaScript 对象,Fiber 的数据结构如下:
type Fiber = {
// 用于标记fiber的WorkTag类型,主要表示当前fiber代表的组件类型如FunctionComponent、ClassComponent等
tag: WorkTag,
// ReactElement里面的key
key: null | string,
// ReactElement.type,调用`createElement`的第一个参数
elementType: any,
// The resolved function/class/ associated with this fiber.
// 表示当前代表的节点类型
type: any,
// 表示当前FiberNode对应的element组件实例
stateNode: any,
// 指向他在Fiber节点树中的`parent`,用来在处理完这个节点之后向上返回
return: Fiber | null,
// 指向自己的第一个子节点
child: Fiber | null,
// 指向自己的兄弟结构,兄弟节点的return指向同一个父节点
sibling: Fiber | null,
index: number,
ref: null | (((handle: mixed) => void) & { _stringRef: ?string }) | RefObject,
// 当前处理过程中的组件props对象
pendingProps: any,
// 上一次渲染完成之后的props
memoizedProps: any,
// 该Fiber对应的组件产生的Update会存放在这个队列里面
updateQueue: UpdateQueue<any> | null,
// 上一次渲染的时候的state
memoizedState: any,
// 一个列表,存放这个Fiber依赖的context
firstContextDependency: ContextDependency<mixed> | null,
mode: TypeOfMode,
// Effect
// 用来记录Side Effect
effectTag: SideEffectTag,
// 单链表用来快速查找下一个side effect
nextEffect: Fiber | null,
// 子树中第一个side effect
firstEffect: Fiber | null,
// 子树中最后一个side effect
lastEffect: Fiber | null,
// 代表任务在未来的哪个时间点应该被完成,之后版本改名为 lanes
expirationTime: ExpirationTime,
// 快速确定子树中是否有不在等待的变化
childExpirationTime: ExpirationTime,
// fiber的版本池,即记录fiber更新过程,便于恢复
alternate: Fiber | null,
}