「源码学习」四、Fiber学习

127 阅读3分钟

React Fiber可以理解为:

React内部实现的一套状态更新机制。支持任务不同优先级,可中断与恢复,并且恢复后可以复用之前的中间状态

Fiber包含三层含义:

  1. 作为架构来说,之前React15Reconciler采用递归的方式执行,数据保存在递归调用栈中,所以被称为stack ReconcilerReact16Reconciler基于Fiber节点实现,被称为Fiber Reconciler
  2. 作为静态的数据结构来说,每个Fiber节点对应一个React element,保存了该组件的类型(函数组件/类组件/原生组件...)、对应的DOM节点等信息。
  3. 作为动态的工作单元来说,每个Fiber节点保存了本次更新中该组件改变的状态、要执行的工作(需要被删除/被插入页面中/被更新...)

Fiber的工作原理或者Fiber是如何更新dom的?

我们在绘制一帧动画的时候,如果当前帧计算量比较大,那么清除上一帧动画到当前帧绘制画面会有较长时间的白屏出现,如果我们内存中绘制当前帧的动画,绘制完毕后直接用当前帧替换上一帧画面,由于省去了两帧替换间的计算时间,就不会出现从白屏。

React使用“双缓存”来完成Fiber树的构建与替换——对应着DOM树的创建与更新。

双缓存Fiber树

React中最多会同时存在两棵Fiber树

当前屏幕上显示内容对应的Fiber树称为current Fiber树,正在内存中构建的Fiber树称为workInProgress Fiber树

React应用的根节点通过使current指针在不同Fiber树rootFiber间切换来完成current Fiber树指向的切换。

即当workInProgress Fiber树构建完成交给Renderer渲染在页面上后,应用根节点的current指针指向workInProgress Fiber树,此时workInProgress Fiber树就变为current Fiber树

每次状态更新都会产生新的workInProgress Fiber树,通过currentworkInProgress的替换,完成DOM更新。

Fiber的节点属性

function FiberNode(
  tag: WorkTag,
  pendingProps: mixed,
  key: null | string,
  mode: TypeOfMode,
) {
  // 作为静态数据结构的属性
  this.tag = tag;
  this.key = key;
  this.elementType = null;
  this.type = null;
  this.stateNode = null;

  // 用于连接其他Fiber节点形成Fiber树
  this.return = null;
  this.child = null;
  this.sibling = null;
  this.index = 0;

  this.ref = null;

  // 作为动态的工作单元的属性
  this.pendingProps = pendingProps;
  this.memoizedProps = null;
  this.updateQueue = null;
  this.memoizedState = null;
  this.dependencies = null;

  this.mode = mode;

  this.effectTag = NoEffect;
  this.nextEffect = null;

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

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

  // 指向该fiber在另一次更新时对应的fiber
  this.alternate = null;
}

组件mount时

  1. 首次执行ReactDOM.render会创建fiberRootNoderootFiber

    • 其中fiberRootNode是整个应用的根节点,rootFiber<App/>所在组件树的根节点。
    • 之所以要区分fiberRootNoderootFiber,是因为在应用中我们可以多次调用ReactDOM.render渲染不同的组件树,他们会拥有不同的rootFiber。但是整个应用的根节点只有一个,那就是fiberRootNode
    • fiberRootNodecurrent会指向当前页面上已渲染内容对应Fiber树,即current Fiber树
    • 由于是首屏渲染,页面中还没有挂载任何DOM,所以fiberRootNode.current指向的rootFiber没有任何子Fiber节点(即current Fiber树为空)。
  2. 接下来进入render阶段,根据组件返回的JSX在内存中依次创建Fiber节点并连接在一起构建Fiber树,被称为workInProgress Fiber树

    • 在构建workInProgress Fiber树时会尝试复用current Fiber树中已有的Fiber节点内的属性,在首屏渲染时只有rootFiber存在对应的current fiber(即rootFiber.alternate)。
  3. 更新完毕之后,fiberRootNodecurrent指针指向workInProgress Fiber树使其变为current Fiber 树

组件Update时

  1. 接下来我们点击节点触发状态改变,这会开启一次新的render阶段并构建一棵新的workInProgress Fiber 树

    mount时一样,workInProgress fiber的创建可以复用current Fiber树对应的节点数据。

  2. workInProgress Fiber 树render阶段完成构建后进入commit阶段渲染到页面上。渲染完毕后,workInProgress Fiber 树变为current Fiber 树