Fiber架构工作原理

925 阅读3分钟

Fiber的含义

Fiber有三层含义

  1. 作为静态数据结构,每个Fiber节点对应一个React Element, 存储了组件类型(函数组件、类组件、原生组件)、以及dom的信息等。
  2. 做为动态工作单元,Fiber节点是每次循环执行的一个节点,在节点上会存储节点的更新信息(增、删、更新)
  3. 作为一种架构,所有的Fiber节点会建立父子兄弟节点之间的关联,形成一个Fiber树,React16中的Reconciler 协调器工作就是基于Fiber节点实现, 所以又叫Fiber Reconciler。

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;
}

Fiber节点如何组织为一颗Fiber树

每个Fiber节点都对应着一个React Element,Fiber节点有三个属性

  this.return = null; //指向父节点
  this.child = null; // 指向子节点
  this.sibling = null; // 指向兄弟节点

所以如下的一个JSX结构生成相应Fiber

function App() {
  return (
    <div>
      i am
      <span>KaSong</span>
    </div>
  )
}

对应Fiber结构

image.png

Fiber结构的工作原理

React采用“双缓存”的技术来创建和更新Fiber树,对应着DOM树的创建与更新。React应用最多会存在2颗Fiber树,一个叫current Fiber树, 代表的是当前页面对应的一个Fiber树, 一个叫workInProgress树,代表的是当前的一个构建树(更新重新构建)。

current Fiber树的的节点叫current Fiber节点, workInprogress Fiber树中的节点叫workInProgress Fiber节点,两者通过alternate相连。

React在应用中会创建一个唯一的FiberRootNode节点,即在第一次调用ReactDom.render时创建, 同时每次使用用ReactDom.render创建一个React App时会生成一个rootFiber节点(相当于是挂载的container节点对应的Fiber节点),React应用的根节点FiberRootNode有个current指针指向当前的current Fiber 树, 当应用更新生成了一颗新的workInProgress Fiber树, 并且完成了commit,更新页面dom后,会将current指向新生成的workInProgress Fiber树, 同时这颗树也就变成了current Fiber 树。如此循环的工作。

React在mount阶段和update阶段构建Fiber树有些区别,如下分别分析2种情况下的一个构建流程:

mount阶段

如下demo:

function App() {
  const [num, add] = useState(0);
  return (
    <p onClick={() => add(num + 1)}>{num}</p>
  )
}

ReactDOM.render(<App/>, document.getElementById('root'));

由于第一次渲染页面,所以此时的current Fiber树没有子节点,如下:

image.png。 当执行render后,会构建workInProgress Fiber树,如下:

image.png

此时页面已经渲染完了, FiberRootNode节点的current属性要指向workInProgress Fiber 树。workInProgress Fiber 树变为current Fiber树。

image.png

update节点

更新阶段由于不是首次渲染页面,因此current Fiber 树是存在的,如上图所示, 所以页面触发更新后, 要生成一颗新的workInProgress Fiber 树, 这个过程中就会有diff算法。

image.png

将新的Fiber树渲染到页面后,又重新对FiberRootNode 的current指向更换, 如此继续循环工作。

参考文档: react.iamkasong.com/