Stack Reconciler的缺点

Stack Reconciler 在渲染和更新过程,使用递归来遍历。由于依赖于内置调用栈,当协调工作开始执行,就会一直执行下去,直到协调工作结束,整个过程无法中断。而js线程和渲染线程互斥,随着应用规模增大,如果在协调算法耗时过长,就会导致应用卡顿,掉帧,影响用户体验。

具体的 Stack Reconciler 算法可以查阅官方文档

Fiber Reconciler

为了可以实现运行时可中断,恢复,停止等操作,React Fiber 底层协调算法引入的一个新的数据结构(链表),基于 Fiber 新的协调算法叫做 Fiber Reconciler 。Fiber Reconciler 将组件的更新任务分解为更小的任务单元,赋予不同的优先级,根据任务的重要性来安排其执行顺序,可以更加高效处理更新任务,实现可中断,可恢复,避免占用 JS 线程导致渲染卡顿。

Fiber对象结构

type Fiber = {|
  tag: WorkTag, // fiber 类型,影响内部逻辑,包含 FunctionComponent
  key: null | string,
  elementType: any, // react 元素类型
  type: any, // 组件构造function / class
  stateNode: any, // host 元素

  return: Fiber | null, // 父节点
  child: Fiber | null, // 子节点
  sibling: Fiber | null, // 兄弟节点
  index: number,

  pendingProps: any, // 待使用的 props
  memoizedProps: any, // 已使用的 props
  updateQueue: mixed, // 状态更新或回调链表
  memoizedState: any, // 状态

  flags: Flags, // fiber 节点包含的副作用标识
  subtreeFlags: Flags, // 子树包含的副作用标识,避免深度遍历
  deletions: Array<Fiber> | null, // 删除的节点,用于执行 unmount 钩子

  lanes: Lanes, // 优先级相关,用于计算过期时间
  childLanes: Lanes,

  alternate: Fiber | null, // current fiber、workInProgress fiber 相互持有对方
|};

这里重点说明,returnsiblingchild 指针三者的关系,举个例子

<App>
  <Header />
  <Body>
    <Content />
  </Body>
</App>

App组件最终生成的fiber树结构如下

调度器

React 使用一种称为调度器(Scheduler)的机制来管理组件的更新和渲染。调度器在 React 中扮演着至关重要的角色,它负责协调和调度组件的更新任务,以保证页面的响应性和性能。

调度器主要有以下功能

1.1 任务调度

调度器负责根据任务的优先级和工作进度来决定任务的执行顺序。它将任务分为不同的优先级,例如高优先级任务和低优先级任务,以便在需要时能够优先处理重要的任务。调度器还可以根据任务的工作进度来划分任务的片段,使得任务的执行可以分散在多个帧中,提高页面的响应性和流畅性。

1.2 中断和恢复任务

调度器具有中断和恢复任务的能力。当用户进行高优先级的交互操作时,调度器可以中断当前任务的执行,立即处理用户操作,以提供更好的用户体验。一旦高优先级任务完成,调度器可以恢复之前被中断的任务的执行,保证任务的连续性和完整性。

1.3 任务优先级调整

调度器可以根据当前系统的资源状况和用户的交互行为来动态调整任务的优先级。例如,当系统资源紧张时,调度器可以降低低优先级任务的执行频率,以释放更多的资源给高优先级任务。这种动态的任务优先级调整可以帮助应用更好地适应不同的环境和场景。

1.4 批量更新

调度器支持批量更新的机制,将多个更新操作合并为一个批量更新任务。这样可以减少不必要的重复渲染和布局计算,提高更新的效率和性能。调度器会在适当的时机执行批量更新任务,以最小化对页面的影响。

总结

React Fiber 是 React 16 中引入的一个重要的协调机制,它通过改变协调算法和引入新的概念,提供了异步渲染、渐进式渲染和错误边界等特性,从而提高了 React 在处理大型应用和复杂场景时的性能和可靠性。了解 React Fiber 的原因、优势和内部实现原理,有助于我们更好地理解和使用 React,优化应用的性能和用户体验。