React 源码讲解第 4 节- Fiber 对象
上一节 React 源码讲解第 2 节-入口 API 之 ReactDOM.render 讲到 render 函数的核心步骤。其中非常重要的一个环节就是创建 Fiber 对象 。本小节介绍 Fiber 对象的含义、类型以及具体包含哪些属性。
Fiber 的三层含义
- 作为架构来说,React 15 的 Reconciler (协调器)采用的是不可中断的递归 。所以被称为【stack Reconciler】,而 React 16 的 Reconciler (协调器)是基于 Fiber 实现的是可中断的循环。所以被称为【Fiber Reconciler】。
- 作为静态的数据结构来说,每一个 element 节点都有一个对应的 Fiber 对象,保存了该组件的类型以及 DOM 节点信息。
- 作为动态的工作单元来说,每一个 Fiber 节点保存了本次更新中该组件改变的状态、要执行的工作等(删除/插入/更新…)。
Fiber 的八种类型
export const FunctionComponent = 0;// 函数类型
export const ClassComponent = 1;// class 类型
export const IndeterminateComponent = 2; // 不确定类型;可能是class或function
export const HostRoot = 3; // 树的根
export const HostPortal = 4; // 一颗子树
export const HostComponent = 5; // 原生节点;根据环境而定,浏览器环境就是div等
export const HostText = 6; // 纯文本节点
export const Fragment = 7; // 节点片段
Fiber 数据结构
function FiberNode(tag, pendingProps, key, mode) {
// Fiber 对应组件的类型 Function/class/ContextProvider
this.tag = tag;
// key属性
this.key = key;
//大部分情况同type,某些情况不同,比如 FunctionComponent 使用React.memo 包裹
this.elementType = null;
// 对于 FunctionComponent,指函数本身,对于ClassComponent,指class,对于HostComponent,指 DOM 节点的tagName
this.type = null;
// Fiber对应的真实DOM节点
this.stateNode = null;
// 以下属性用于连接其他Fiber节点形成Fiber树。
// 指向父Fiber节点
this.return = null;
//指向第一个子Fiber节点
this.child = null;
//指向右边第一个兄弟Fiber节点
this.sibling = null;
this.index = 0;
// 获取真实节点的 DOM 属性
this.ref = null;
// 动态的工作单元属性。保存本次更新造成的状态改变相关信息
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
//开发模式
this.mode = mode; // Effects
// 保存本次更新会造成的DOM操作。比如删除,移动
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
// 调度优先级相关
this.expirationTime = NoWork;
this.childExpirationTime = NoWork;
// 指向该fiber在另一次更新时对应的fiber,
//用于链接新树和旧树;旧->新,新->旧
this.alternate = null;
{
// Note: The following is done to avoid a v8 performance cliff.
//
// Initializing the fields below to smis and later updating them with
// double values will cause Fibers to end up having separate shapes.
// This behavior/bug has something to do with Object.preventExtension().
// Fortunately this only impacts DEV builds.
// Unfortunately it makes React unusably slow for some applications.
// To work around this, initialize the fields below with doubles.
//
// Learn more about this here:
// https://github.com/facebook/react/issues/14365
// https://bugs.chromium.org/p/v8/issues/detail?id=8538
this.actualDuration = Number.NaN;
this.actualStartTime = Number.NaN;
this.selfBaseDuration = Number.NaN;
this.treeBaseDuration = Number.NaN; // It's okay to replace the initial doubles with smis after initialization.
// This won't trigger the performance cliff mentioned above,
// and it simplifies other profiler code (including DevTools).
this.actualDuration = 0;
this.actualStartTime = -1;
this.selfBaseDuration = 0;
this.treeBaseDuration = 0;
} // This is normally DEV-only except www when it adds listeners.
// TODO: remove the User Timing integration in favor of Root Events.
{
this._debugID = debugCounter++;
this._debugIsCurrentlyTiming = false;
}
{
this._debugSource = null;
this._debugOwner = null;
this._debugNeedsRemount = false;
this._debugHookTypes = null;
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
Object.preventExtensions(this);
}
}
}
开发模式
本源码讲解的是最新版本 React 16.13.1。到目前版本为止,内置的开发模式有如下几种。其中最常用的是严格模式 StrictMode。
export type TypeOfMode = number;
// 普通模式|Legacy模式,同步渲染,React15-16的生产环境用
export const NoMode = 0b0000;
// 严格模式,用来检测是否存在废弃API(会多次调用渲染阶段生命周期),React16-17开发环境使用
export const StrictMode = 0b0001;
// ConcurrentMode 模式的过渡版本
export const BlockingMode = 0b0010;
// 并发模式,异步渲染,React17的生产环境用
export const ConcurrentMode = 0b0100;
// 性能测试模式,用来检测哪里存在性能问题,React16-17开发环境使用
export const ProfileMode = 0b1000;