React源码分析2-深入理解fiber

react16 版本之后引入了 fiber,整个架构层面的 调度、协调、diff 算法以及渲染等都与 fiber 密切相关。所以为了更好地讲解后面的内容,需要对 fiber 有个比较清晰的认知。本章将介绍以下内容:

  • 为什么需要 fiber
  • fiber 节点结构中的属性
  • fiber 树是如何构建与更新的

为什么需要 fiber

Lin Clark 在 React Conf 2017 的演讲中,他通过漫画的形式,很好地讲述了 fiber 为何出现,下面我根据她的演讲,结合我自己的理解来谈一谈 fiber 出现的原因。

fiber 之前

在 react15 及之前 fiber 未出现时,react 的一系列执行过程例如生命周期执行、虚拟 dom 的比较、dom 树的更新等都是同步的,一旦开始执行就不会中断,直到所有的工作流程全部结束为止。

要知道,react 所有的状态更新,都是从根组件开始的,当应用组件树比较庞大时,一旦状态开始变更,组件树层层递归开始更新,js 主线程就不得不停止其他工作。例如组件树一共有 1000 个组件需要更新,每个组件更新所需要的时间为 1s,那么在这 1s 内浏览器都无法做其他的事情,用户的点击输入等交互事件、页面动画等都不会得到响应,体验就会非常的差。

这种情况下,函数堆栈的调用就像下图一样,层级很深,很长时间不会返回

fiber 之后

为了解决这一问题,react 引入了 fiber 这种数据结构,将更新渲染耗时长的大任务,分为许多的小片。每个小片的任务执行完成后,都先去执行其他高优先级的任务(例如用户点击输入事件、动画等),这样 js 的主线程就不会被 react 独占,虽然任务执行的总时间不变,但是页面能够及时响应高优先级任务,显得不会卡顿了。

fiber 分片模式下,浏览器主线程能够定期被释放,保证了渲染的帧率,函数的堆栈调用如下(波谷表示执行分片任务,波峰表示执行其他高优先级任务): React源码分析2-深入理解fiber_第1张图片 react 通过 fiber,为我们提供了一种跟踪、调度、暂停和中止工作的便捷方式,保证了页面的性能和流畅度。

fiber 节点结构

fiber 是一种数据结构,每个 fiber 节点的内部,都保存了 dom 相关信息、fiber 树相关的引用、要更新时的副作用等,我们可以看一下源码中的 fiber 结构:

// packages/react-reconciler/src/ReactInternalTypes.js

export type Fiber = {
   |
  // 作为静态数据结构,存储节点 dom 相关信息
  tag: WorkTag, // 组件的类型,取决于 react 的元素类型
  key: null | string,
  elementType: any, // 元素类型
  type: any, // 定义与此fiber关联的功能或类。对于组件,它指向构造函数;对于DOM元素,它指定HTML tag
  stateNode: any, // 真实 dom 节点

  // fiber 链表树相关
  return: Fiber | null, // 父 fiber
  child: Fiber | null, // 第一个子 fiber
  sibling: Fiber | null, // 下一个兄弟 fiber
  index: number, // 在父 fiber 下面的子 fiber 中的下标

  ref:
    | null
    | (((handle: mixed) => void) & {
   _stringRef: ?string, ...})
    | RefObject,

  // 工作单元,用于计算 state 和 props 渲染
  pendingProps: any, // 本次渲染需要使用的 props
  memoizedProps: any, // 上次渲染使用的 props
  updateQueue: mixed, // 用于状态更新、回调函数、DOM更新的队列
  memoizedState: any, // 上次渲染后的 state 状态
  dependencies: Dependencies | null, // contexts、events 等依赖

  mode: TypeOfMode,

  // 副作用相关
  flags: Flags, // 记录更新时当前 fiber 的副作用(删除、更新、替换等)状态
  subtreeFlags: Flags, // 当前子树的副作用状态
  deletions: Array<Fiber> | null, // 要删除的子 fiber
  nextEffect: Fiber | null, // 下一个有副作用的 fiber
  firstEffect: Fiber | null, // 指向第一个有副作用的 fiber
  lastEffect: Fiber | null, // 指向最后一个有副作用的 fiber 

  // 优先级相关
  lanes: Lanes,
  childLanes: Lanes,

  alternate: Fiber | null, // 指向 workInProgress fiber 树中对应的节点

  actualDuration?: number,
  actualStartTime?: number,
  selfBaseDuration?: number,
  treeBaseDuration?: number,
  _debugID?: number,
  _debugSource?: Source | null,
  _debugOwner?: Fiber | null,
  _debugIsCurrentlyTiming?: boolean,
  _debugNeedsRemount?: boolean,
  _debugHookTypes?: Array<HookType> | null,
|};

dom 相关属性

fiber 中和 dom 节点相关的信息主要关注 tagkeytypestateNode

tag

fiber 中 tag 属性的 ts 类型为 workType,用于标记不同的 react 组件类型,我们可以看一下源码中 workType 的枚举值:

// packages/react-reconciler/src/ReactWorkTags.js

export const FunctionComponent = 0;
export const ClassComponent = 1;
export const IndeterminateComponent = 2; // Before we know whether it is function or class
export const HostRoot = 3; // Root of a host tree. Could be nested inside another node.

你可能感兴趣的:(reactjs)