Fiber
一、作用
二、有坏的 Map 填充
let hasBadMapPolyfill;
if (__DEV__) {
hasBadMapPolyfill = false;
try {
const nonExtensibleObject = Object.preventExtensions({});
new Map([[nonExtensibleObject, null]]);
new Set([nonExtensibleObject]);
} catch (e) {
// TODO: Consider warning about bad polyfills
// 待办:考虑提醒有问题的填充功能
hasBadMapPolyfill = true;
}
}
三、组件类型判定
1. 是简单函数组件
export function isSimpleFunctionComponent(type: any): boolean {
return (
typeof type === 'function' &&
!shouldConstruct(type) &&
type.defaultProps === undefined
);
}
2. 是函数式类组件
export function isFunctionClassComponent(
type: (...args: Array<any>) => mixed,
): boolean {
return shouldConstruct(type);
}
四、备用 Fiber
1. 构建备用 Fiber
构建备用的的渲染的工作树,非当前实际渲染的状态树
信息
NoFlags、StaticMask来自于文件 ReactFiberFlags
// This is used to create an alternate fiber to do work on.
// 这用于创建一个备用 fiber 来执行工作。
export function createWorkInProgress(current: Fiber, pendingProps: any): Fiber {
let workInProgress = current.alternate;
if (workInProgress === null) {
// We use a double buffering pooling technique because we know that we'll
// only ever need at most two versions of a tree. We pool the "other" unused
// node that we're free to reuse. This is lazily created to avoid allocating
// extra objects for things that are never updated. It also allow us to
// reclaim the extra memory if needed.
//
// 我们使用双缓冲池技术,因为我们知道最多只需要两棵树的版本。
// 我们会池化“其他”的未使用节点,以便可以重用。
// 这是懒创建的,以避免为从未更新的对象分配额外的资源。
// 它还允许我们在需要时回收额外的内存。
workInProgress = createFiber(
current.tag,
pendingProps,
current.key,
current.mode,
);
workInProgress.elementType = current.elementType;
workInProgress.type = current.type;
workInProgress.stateNode = current.stateNode;
if (__DEV__) {
// DEV-only fields
// 仅供开发使用的字段
workInProgress._debugOwner = current._debugOwner;
workInProgress._debugStack = current._debugStack;
workInProgress._debugTask = current._debugTask;
workInProgress._debugHookTypes = current._debugHookTypes;
}
workInProgress.alternate = current;
current.alternate = workInProgress;
} else {
workInProgress.pendingProps = pendingProps;
// Needed because Blocks store data on type.
// 需要,因为块会根据类型存储数据。
workInProgress.type = current.type;
// We already have an alternate.
// Reset the effect tag.
// 我们已经有一个替代方案。
// 重置效果标签。
workInProgress.flags = NoFlags;
// The effects are no longer valid.
// 这些效果不再有效。
workInProgress.subtreeFlags = NoFlags;
workInProgress.deletions = null;
if (enableProfilerTimer) {
// We intentionally reset, rather than copy, actualDuration & actualStartTime.
// This prevents time from endlessly accumulating in new commits.
// This has the downside of resetting values for different priority renders,
// But works for yielding (the common case) and should support resuming.
//
// 我们故意重置,而不是复制 actualDuration 和 actualStartTime。
// 这样可以防止时间在新的提交中无休止地累积。
// 这样做的缺点是会重置不同优先级渲染的值,
// 但对于让步情况(常见情况)有效,并且应该支持继续执行。
workInProgress.actualDuration = -0;
workInProgress.actualStartTime = -1.1;
}
}
// Reset all effects except static ones.
// Static effects are not specific to a render.
//
// 重置所有效果,静态效果除外。
// 静态效果与渲染无关。
workInProgress.flags = current.flags & StaticMask;
workInProgress.childLanes = current.childLanes;
workInProgress.lanes = current.lanes;
workInProgress.child = current.child;
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;
// Clone the dependencies object. This is mutated during the render phase, so
// it cannot be shared with the current fiber.
//
// 克隆 dependencies 对象。该对象在渲染阶段会被修改,
// 因此不能与当前 fiber 共享。
const currentDependencies = current.dependencies;
workInProgress.dependencies =
currentDependencies === null
? null
: __DEV__
? {
lanes: currentDependencies.lanes,
firstContext: currentDependencies.firstContext,
_debugThenableState: currentDependencies._debugThenableState,
}
: {
lanes: currentDependencies.lanes,
firstContext: currentDependencies.firstContext,
};
// These will be overridden during the parent's reconciliation
// 这些将在父组件的协调过程中被覆盖
workInProgress.sibling = current.sibling;
workInProgress.index = current.index;
workInProgress.ref = current.ref;
workInProgress.refCleanup = current.refCleanup;
if (enableProfilerTimer) {
workInProgress.selfBaseDuration = current.selfBaseDuration;
workInProgress.treeBaseDuration = current.treeBaseDuration;
}
if (__DEV__) {
workInProgress._debugInfo = current._debugInfo;
workInProgress._debugNeedsRemount = current._debugNeedsRemount;
switch (workInProgress.tag) {
case FunctionComponent:
case SimpleMemoComponent:
workInProgress.type = resolveFunctionForHotReloading(current.type);
break;
case ClassComponent:
workInProgress.type = resolveClassForHotReloading(current.type);
break;
case ForwardRef:
workInProgress.type = resolveForwardRefForHotReloading(current.type);
break;
default:
break;
}
}
return workInProgress;
}
2. 重置备用 Fiber
// Used to reuse a Fiber for a second pass.
// 用于将一个 Fiber 复用在第二次遍历中。
export function resetWorkInProgress(
workInProgress: Fiber,
renderLanes: Lanes,
): Fiber {
// This resets the Fiber to what createFiber or createWorkInProgress would
// have set the values to before during the first pass. Ideally this wouldn't
// be necessary but unfortunately many code paths reads from the workInProgress
// when they should be reading from current and writing to workInProgress.
//
// 这会将 Fiber 重置为 createFiber 或 createWorkInProgress 在第一次遍历时设置的值。
// 理想情况下,这本不应该是必要的,但不幸的是,许多代码路径在本应从 current 读取并写入
// workInProgress 时,却读取了 workInProgress。
// We assume pendingProps, index, key, ref, return are still untouched to
// avoid doing another reconciliation.
//
// 我们假设 pendingProps、index、key、ref、return 仍然保持不变,以避免进行另一次协调。
// Reset the effect flags but keep any Placement tags, since that's something
// that child fiber is setting, not the reconciliation.
//
// 重置效果标志,但保留任何 Placement 标签,
// 因为这是子 Fiber 设置的,而不是协调过程设置的。
workInProgress.flags &= StaticMask | Placement;
// The effects are no longer valid.
// 这些效果不再有效。
const current = workInProgress.alternate;
if (current === null) {
// Reset to createFiber's initial values.
// 重置为 createFiber 的初始值。
workInProgress.childLanes = NoLanes;
workInProgress.lanes = renderLanes;
workInProgress.child = null;
workInProgress.subtreeFlags = NoFlags;
workInProgress.memoizedProps = null;
workInProgress.memoizedState = null;
workInProgress.updateQueue = null;
workInProgress.dependencies = null;
workInProgress.stateNode = null;
if (enableProfilerTimer) {
// Note: We don't reset the actualTime counts. It's useful to accumulate
// actual time across multiple render passes.
// 注意:我们不会重置 actualTime 计数。
// 这对于在多次渲染过程中累积实际时间很有用。
workInProgress.selfBaseDuration = 0;
workInProgress.treeBaseDuration = 0;
}
} else {
// Reset to the cloned values that createWorkInProgress would've.
// 重置为 createWorkInProgress 会创建的克隆值。
workInProgress.childLanes = current.childLanes;
workInProgress.lanes = current.lanes;
workInProgress.child = current.child;
workInProgress.subtreeFlags = NoFlags;
workInProgress.deletions = null;
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;
// Needed because Blocks store data on type.
// 需要,因为块会根据类型存储数据。
workInProgress.type = current.type;
// Clone the dependencies object. This is mutated during the render phase, so
// it cannot be shared with the current fiber.
//
// 克隆依赖对象。它会在渲染阶段被修改,
// 因此不能与当前 fiber 共享。
const currentDependencies = current.dependencies;
workInProgress.dependencies =
currentDependencies === null
? null
: __DEV__
? {
lanes: currentDependencies.lanes,
firstContext: currentDependencies.firstContext,
_debugThenableState: currentDependencies._debugThenableState,
}
: {
lanes: currentDependencies.lanes,
firstContext: currentDependencies.firstContext,
};
if (enableProfilerTimer) {
// Note: We don't reset the actualTime counts. It's useful to accumulate
// actual time across multiple render passes.
//
// 注意:我们不会重置 actualTime 计数。这对于在多次渲染过程中累积实际时间很有用。
workInProgress.selfBaseDuration = current.selfBaseDuration;
workInProgress.treeBaseDuration = current.treeBaseDuration;
}
}
return workInProgress;
}
五、构建主机根 Fiber
export function createHostRootFiber(
tag: RootTag,
isStrictMode: boolean,
): Fiber {
let mode: number;
// 禁用遗留模式 or 并发根
if (disableLegacyMode || tag === ConcurrentRoot) {
mode = ConcurrentMode;
if (isStrictMode === true) {
mode |= StrictLegacyMode | StrictEffectsMode;
}
} else {
mode = NoMode;
}
if (__DEV__ || (enableProfilerTimer && isDevToolsPresent)) {
// dev: Enable profiling instrumentation by default.
// profile: enabled if DevTools is present or subtree is wrapped in <Profiler>.
// production: disabled.
//
// 开发:默认启用性能分析工具。
// 分析:如果存在开发者工具或子树被 <Profiler> 包裹,则启用。
// 生产:禁用。
mode |= ProfileMode;
}
return createFiber(HostRoot, null, null, mode);
}
工具
1.创建 Fiber
信息
enableObjectFiber: 将 Fiber 的创建切换为使用简单对象而不是构造函数。
实际上 Fiber 的构建有两种方式,可通过特定平台设定 enableObjectFiber 值来切换。或者是开发者想看一看(水代码)那个实现方式更好
function FiberNode(
this: $FlowFixMe,
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
) {
// Instance
// 实例
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.refCleanup = null;
this.pendingProps = pendingProps;
this.memoizedProps = null;
this.updateQueue = null;
this.memoizedState = null;
this.dependencies = null;
this.mode = mode;
// Effects
// 效果(副作用们)
this.flags = NoFlags;
this.subtreeFlags = NoFlags;
this.deletions = null;
this.lanes = NoLanes;
this.childLanes = NoLanes;
this.alternate = null;
if (enableProfilerTimer) {
// Note: The following is done to avoid a v8 performance cliff.
// 注意:以下操作是为了避免 v8 性能下降。
//
// 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.
//
// 将下面的字段初始化为smis,然后再用双精度值更新它们,会导致Fibers最终具有不同的形状。
// 这种行为/错误与Object.preventExtension()有关。
// 幸运的是,这只影响开发版本。
// 不幸的是,这会使某些应用中的React变得非常慢。
// 为了解决这个问题,请用双精度值初始化下面的字段。
//
// Learn more about this here:
// https://github.com/facebook/react/issues/14365
// https://bugs.chromium.org/p/v8/issues/detail?id=8538
this.actualDuration = -0;
this.actualStartTime = -1.1;
this.selfBaseDuration = -0;
this.treeBaseDuration = -0;
}
if (__DEV__) {
// This isn't directly used but is handy for debugging internals:
// 这不是直接使用的,但对于调试内部功能很有用:
this._debugInfo = null;
this._debugOwner = null;
this._debugStack = null;
this._debugTask = null;
this._debugNeedsRemount = false;
this._debugHookTypes = null;
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
Object.preventExtensions(this);
}
}
}
// This is a constructor function, rather than a POJO constructor, still
// please ensure we do the following:
// 1) Nobody should add any instance methods on this. Instance methods can be
// more difficult to predict when they get optimized and they are almost
// never inlined properly in static compilers.
// 2) Nobody should rely on `instanceof Fiber` for type testing. We should
// always know when it is a fiber.
// 3) We might want to experiment with using numeric keys since they are easier
// to optimize in a non-JIT environment.
// 4) We can easily go from a constructor to a createFiber object literal if that
// is faster.
// 5) It should be easy to port this to a C struct and keep a C implementation
// compatible.
//
// 这是一个构造函数,而不是普通的 POJO 构造函数,仍然
// 请确保我们做到以下几点:
// 1)不要有人在此添加任何实例方法。实例方法在优化时可能更难预测,
// 并且在静态编译器中几乎从不正确内联。
// 2)不要有人依赖 `instanceof Fiber` 进行类型测试。我们应该
// 总是知道何时它是 fiber。
// 3)我们可能想尝试使用数字键,因为它们在非 JIT 环境中更容易优化。
// 4)如果这样更快,我们可以轻松地从构造函数转换为 createFiber 对象字面量。
// 5)将其移植到 C 结构并保持与 C 实现兼容应该很容易。
function createFiberImplClass(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
): Fiber {
return new FiberNode(tag, pendingProps, key, mode);
}
// 创建 Fiber 实现对象
function createFiberImplObject(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
): Fiber {
const fiber: Fiber = {
// Instance
// 实例
// tag, key - defined at the bottom as dynamic properties
// 标签,键 - 在底部作为动态属性定义
elementType: null,
type: null,
stateNode: null,
// Fiber
// Fiber
return: null,
child: null,
sibling: null,
index: 0,
ref: null,
refCleanup: null,
// pendingProps - defined at the bottom as dynamic properties
// pendingProps - 在底部定义为动态属性
memoizedProps: null,
updateQueue: null,
memoizedState: null,
dependencies: null,
// Effects
// 影响(副作用们)
flags: NoFlags,
subtreeFlags: NoFlags,
deletions: null,
lanes: NoLanes,
childLanes: NoLanes,
alternate: null,
// dynamic properties at the end for more efficient hermes bytecode
// 在末尾使用动态属性以提高 Hermes 字节码的效率
tag,
key,
pendingProps,
mode,
};
if (enableProfilerTimer) {
fiber.actualDuration = -0; // 实际持续时间
fiber.actualStartTime = -1.1; // 实际开始时间
fiber.selfBaseDuration = -0; // 自身基础持续时间
fiber.treeBaseDuration = -0; // 树基础持续时间
}
if (__DEV__) {
// This isn't directly used but is handy for debugging internals:
// 这不是直接使用的,但对于调试内部功能很方便:
fiber._debugInfo = null;
fiber._debugOwner = null;
fiber._debugStack = null;
fiber._debugTask = null;
fiber._debugNeedsRemount = false;
fiber._debugHookTypes = null;
if (!hasBadMapPolyfill && typeof Object.preventExtensions === 'function') {
Object.preventExtensions(fiber);
}
}
return fiber;
}
/**
*
* - enableObjectFiber : 将 Fiber 的创建切换为使用简单对象而不是构造函数。
*
*
*/
const createFiber = enableObjectFiber
? createFiberImplObject
: createFiberImplClass;
2. 应该构建
function shouldConstruct(Component: Function) {
const prototype = Component.prototype;
return !!(prototype && prototype.isReactComponent);
}