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 是如何和组件联系的呢?并且如何实现效果的呢?
React App 中的基础单元是组件,应用以组件树形式组织,渲染组件;
Fiber reconciliation 基础单元则是fiber(调和单元),应用以fiber树形式组织,应用Fiber算法;
组件树和fiber树结构对应,一个组件实例有一个对应的fiber实例;
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<any> | 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.
|};
type & key:同React元素的值;
-
type:描述fiber对应的React组件;
2.1 对于组合组件:值为function或class组件本身;
2.2 对于原生组件(div等):值为该元素类型字符串;
key:调和阶段,标识fiber,以检测是否可重用该fiber实例;
child & sibling:组件树,对应生成fiber树,类比的关系;
pendingProps & memoizedProps:分别表示组件当前传入的及之前的props;
return:返回当前fiber所在fiber树的父级fiber实例,即当前组件的父组件对应的fiber;
alternate:fiber的版本池,即记录fiber更新过程,便于恢复重用;
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;
}
参考文章: