react源码阅读- fiber架构探索(一)

react源码阅读- fiber架构探索(一)

React 团队在 React 的v16版本中重写了 React 的核心算法 - reconciliation,称为fiber reconciler,简称为Fiber。

优势

  • 可中断拆分任务

  • render 函数可以返回多个元素(即可以返回数组)

  • 对执行的任务划分优先级,可复用上一次的工作成果

  • 更加友好的支持 error boundary,异常边界处理

  • 可在父子组件任务之间切换,支持执行过程中的布局刷新

Fiber 给 React 带来了更加先进的调和器,它允许渲进程分段进行,也可以再执行的过程中返回主进程执行其他任务,非常的灵活,而这是通过diff算法,计算部分组件树的变更,并暂停渲染更新,询问主进程是否有更高需求的绘制或者更新任务需要执行,也就是任务之间是有优先级的;这一切的实现是通过引入新的数据结构-fiber对象来实现的,一个组件实例对应一个fiber实例,fiber实例负责管理组件实例的更新,渲染任务及与其他fiber实例的联系。

关键点

Fiber 的主要特点是:

  • 给不同类型的更新赋予优先级

  • 并发方面新的基础能力

  • 把渲染任务拆分成块,匀到多帧(增量渲染)

  • 更新时能够暂停,终止,复用渲染任务

增量渲染用来解决掉帧的问题,渲染任务拆分之后,每次只做一小段,做完一段就把时间控制权交还给主线程,而不像之前长时间占用。这种策略叫做cooperative scheduling(合作式调度),操作系统的3种任务调度策略之一。

Fiber 与组件

Fiber 是如何和组件联系的呢?并且如何实现效果的呢?

  1. React App 中的基础单元是组件,应用以组件树形式组织,渲染组件;

  2. Fiber reconciliation 基础单元则是fiber(调和单元),应用以fiber树形式组织,应用Fiber算法;

  3. 组件树和fiber树结构对应,一个组件实例有一个对应的fiber实例;

  4. Fiber负责整个应用层面的调和,fiber实例负责对应组件的调和;

注意Fiber与fiber的区别,Fiber是指reconciliation算法,fiber则是reconciliation算法组成单元,和组件与应用关系类似,每一个组件实例会有对应的fiber实例负责该组件的调和。

Fiber 数据结构

接下来让看看Fiber的数据结构,数据结构能在一定程度反映其整体工作架构。

一个fiber就是一个javascript对象,已键值对存储一个关联组件的对象,包括组件的props属性,维护的state,最后需要渲染出的内容等。

Fiber 对象


// 一个Fiber对象作用于一个组件

export type Fiber = {|

  // 标记fiber类型tag.

  tag: TypeOfWork,

  // fiber对应的function/class/module类型组件名.

  type: any,

  // fiber所在组件树的根组件FiberRoot对象

  stateNode: any,

  // 处理完当前fiber后返回的fiber,

  // 返回当前fiber所在fiber树的父级fiber实例

  return: Fiber | null,

  // fiber树结构相关链接

  child: Fiber | null,

  sibling: Fiber | null,

  index: number,

  // 当前处理过程中的组件props对象

  pendingProps: any,

  // 缓存的之前组件props对象

  memoizedProps: any, // The props used to create the output.

  // The state used to create the output

  memoizedState: any,

  // 组件状态更新及对应回调函数的存储队列

  updateQueue: UpdateQueue | null,

  // 描述当前fiber实例及其子fiber树的数位,

  // 如,AsyncUpdates特殊字表示默认以异步形式处理子树,

  // 一个fiber实例创建时,此属性继承自父级fiber,在创建时也可以修改值,

  // 但随后将不可修改。

  internalContextTag: TypeOfInternalContext,

  // 更新任务的最晚执行时间

  expirationTime: ExpirationTime,

  // fiber的版本池,即记录fiber更新过程,便于恢复

  alternate: Fiber | null,

  // Conceptual aliases

  // workInProgress : Fiber ->  alternate The alternate used for reuse happens

  // to be the same as work in progress.

|};

  1. type & key:同React元素的值;

  2. type:描述fiber对应的React组件;

    2.1 对于组合组件:值为function或class组件本身;

    2.2 对于原生组件(div等):值为该元素类型字符串;

  3. key:调和阶段,标识fiber,以检测是否可重用该fiber实例;

  4. child & sibling:组件树,对应生成fiber树,类比的关系;

  5. pendingProps & memoizedProps:分别表示组件当前传入的及之前的props;

  6. return:返回当前fiber所在fiber树的父级fiber实例,即当前组件的父组件对应的fiber;

  7. alternate:fiber的版本池,即记录fiber更新过程,便于恢复重用;

  8. workInProgress:正在处理的fiber,概念上叫法,实际上没有此属性;

Fiber类型

Fiber对象中有个tag属性,标记fiber类型,而fiber实例是和组件对应的,所以其类型基本上对应于组件类型,源码见ReactTypeOfWork模块:


export type TypeOfWork = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10;

export const IndeterminateComponent = 0; // 尚不知是类组件还是函数式组件

export const FunctionalComponent = 1; // 函数式组件

export const ClassComponent = 2; // Class类组件

export const HostRoot = 3; // 组件树根组件,可以嵌套

export const HostPortal = 4; // 子树. Could be an entry point to a different renderer.

export const HostComponent = 5; // 标准组件,如地div, span等

export const HostText = 6; // 文本

export const CallComponent = 7; // 组件调用

export const CallHandlerPhase = 8; // 调用组件方法

export const ReturnComponent = 9; // placeholder(占位符)

export const Fragment = 10; // 片段

在调度执行任务的时候会根据不同类型fiber,即fiber.tag值进行不同处理。

FiberRoot对象

FiberRoot对象,主要用来管理组件树组件的更新进程,同时记录组件树挂载的DOM容器相关信息,具体定义见ReactFiberRoot模块:


export type FiberRoot = {

  // fiber节点的容器元素相关信息,通常会直接传入容器元素

  containerInfo: any,

  // 当前fiber树中激活状态(正在处理)的fiber节点,

  current: Fiber,

  // 此节点剩余的任务到期时间

  remainingExpirationTime: ExpirationTime,

  // 更新是否可以提交

  isReadyForCommit: boolean,

  // 准备好提交的已处理完成的work-in-progress

  finishedWork: Fiber | null,

  // 多组件树FirberRoot对象以单链表存储链接,指向下一个需要调度的FiberRoot

  nextScheduledRoot: FiberRoot | null,

};

创建FIBERROOT实例


import {

  ClassComponent,

  HostRoot

} from 'shared/ReactTypeOfWork';

// 创建返回一个初始根组件对应的fiber实例

function createHostRootFiber(): Fiber {

  // 创建fiber

  const fiber = createFiber(HostRoot, null, NoContext);

  return fiber;

}

export function createFiberRoot(

  containerInfo: any,

  hydrate: boolean,

) {

  // 创建初始根组件对应的fiber实例

  const uninitializedFiber = createHostRootFiber();

  // 组件树根组件的FiberRoot对象

  const root = {

    // 根组件对应的fiber实例

    current: uninitializedFiber,

    containerInfo: containerInfo,

    pendingChildren: null,

    remainingExpirationTime: NoWork,

    isReadyForCommit: false,

    finishedWork: null,

    context: null,

    pendingContext: null,

    hydrate,

    nextScheduledRoot: null,

  };

  // 组件树根组件fiber实例的stateNode指向FiberRoot对象

  uninitializedFiber.stateNode = root;

  return root;

}

参考文章:

  1. react

  2. reconciliation

你可能感兴趣的:(react源码阅读- fiber架构探索(一))