React源码学习——Fiber的基本概念

118 阅读2分钟

Fiber节点的结构

路径:react-reconciler/src/ReacFiber.old.js

fiber节点主要包含四部分的数据:静态节点相关、Fiber树结构相关、更新状态相关、调度优先级相关

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // 静态节点相关属性,与react element对应
  this.tag = tag; // 组件类型 Host、Function、Class等
  this.key = key;
  this.elementType = null;
  this.type = null; // Host是DOM节点的tagName,Function是函数自身,Class是class...
  this.stateNode = null; // 对应的DOM节点

  // Fiber树结构相关的属性
  this.return = null; // 父fiber节点(绝大多数情况下),或是上次中断后,需要恢复执行的节点
  this.child = null; // 第一个fiber子节点
  this.sibling = null; // 第一个兄弟fiber节点
  this.index = 0;

  this.ref = null;

  // 更新状态相关属性
  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  // Effects
  this.flags = NoFlags;
  this.nextEffect = null;

  this.firstEffect = null;
  this.lastEffect = null;

  // 调度优先级相关
  this.lanes = NoLanes;
  this.childLanes = NoLanes;

  // 更新时,另一颗fiber树上对应该节点的fiber节点
  this.alternate = null;
}

Fiber不仅是对于原生DOM的一种虚拟DOM的表现形式。它还保存了更新状态和调度相关的信息,是react更新机制的动态的工作单元

工作流程

先抛开调度的机制,看源码中从Fiber到最终渲染到页面上的基本流程

Render阶段

react-reconciler/src/ReactFiberWorkLoop.old.js中,会判断是否有需要执行的任务(workInProgress),如果有就调用performUnitOfWork。它接收当前需要执行的Fiber。主要代码如下:

function performUnitOfWork(unitOfWork: Fiber): void {
  const current = unitOfWork.alternate;

  let next;
  // ...省略部分代码
  next = beginWork(current, unitOfWork, subtreeRenderLanes);
  // ...省略部分代码
  unitOfWork.memoizedProps = unitOfWork.pendingProps;
  if (next === null) {
    completeUnitOfWork(unitOfWork);
  } else {
    workInProgress = next;
  }

  ReactCurrentOwner.current = null;
}

performUnitOfWork以深度优先遍历的方式,对各节点主要做了四件事,最终得到需要修改的虚拟DOM:

  1. 将当前的操作节点,换成与当前Fiber对应的更新的Fiber节点(双缓存Fiber树机制)
  2. 当前节点执行beginWork,返回当前节点的下一个节点
  3. 更改当前节点属性的状态
  4. 判断是否还有需要处理的节点,如果没有则调用completeUnitOfWork,如果有则继续循环操作下一个节点

commit阶段

遍历完所有节点后,进入Renderer的部分,commit阶段,react-reconciler/src/ReactFiberWorkLoop.old.js下的commitRoot方法。其主要调用了commitRootImpl,该方法很长,拆分一下主要分为五个部分,不同阶段都会对应执行一些生命周期的钩子,DOM节点的相关操作,以及Hooks的调度:

  1. before mutation前
  2. before mutation
  3. mutation
  4. layout
  5. layout后

总结

Fiber的大体流程是通过调度器(Scheduler)将任务交给协调器(Reconciler),协调器遍历处理所有任务,得到需要修改的虚拟DOM。然后交给Renderer(渲染器)进行DOM操作。

接下来会详细去看Render阶段的beginWork和completeWork,commit的各阶段的工作。了解了整个流程后,再看scheduler的调度机制。