packages/react-reconciler/src/ReactFiber.js
Fiber
// A Fiber is work on a Component that needs to be done or was done. There can
// be more than one per component.
// Fiber 是 Component 上需要完成或已经完成工作。每个组件可以有多个 fiber。
export type Fiber = {|
// These first fields are conceptually members of an Instance. This used to
// be split into a separate type and intersected with the other Fiber fields,
// but until Flow fixes its intersection bugs, we've merged them into a
// single type.
// 这些字段在概念上是实例的成员。
// 这曾经被分割成一个单独的类型,并与其他 fiber 字段相交,
// 但是直到 Flow 修复了它的交集错误,我们才将它们合并成一个单一的类型。
// An Instance is shared between all versions of a component. We can easily
// break this out into a separate object to avoid copying so much to the
// alternate versions of the tree. We put this on a single object for now to
// minimize the number of objects created during the initial render.
// 在一个组件的所有版本之间共享的实例。
// 我们可以很容易地将其拆分为一个单独的对象,
// 以避免过多地复制到树的其他版本。
// 现在我们将它放在一个对象上,以最小化在初始渲染期间创建的对象的数量。
// 标记 fiber 类型的标签。
tag: WorkTag,
// 此子元素的唯一标识符。
key: null | string,
// The value of element.type which is used to preserve the identity during
// reconciliation of this child.
// element.type 的值,用于在此子元素的协调(reconciliation)过程中保存标识。
elementType: any,
// The resolved function/class/ associated with this fiber.
//与此 fiber 相关联的已解析的 function/class/。
type: any,
// The local state associated with this fiber.
// 与此 fiber 相关联的本地状态。
stateNode: any,
// Conceptual aliases
// parent : Instance -> return The parent happens to be the same as the
// return fiber since we've merged the fiber and instance.
// 概念的别名
// parent:Instance -> return
// parent 恰好与 return 的 fiber 相同,因为我们已经合并了 fiber 和实例。
// Remaining fields belong to Fiber
// 剩余的字段属于 Fiber
// The Fiber to return to after finishing processing this one.
// This is effectively the parent, but there can be multiple parents (two)
// so this is only the parent of the thing we're currently processing.
// It is conceptually the same as the return address of a stack frame.
// 这条 Fiber 处理完后完后要返回。
// 这实际上是 parent,但是可以有多个 parent (两个),所以这只是我们当前正在处理的东西的父类 fiber。
// 在概念上与堆栈帧的返回地址相同。
return: Fiber | null,
// Singly Linked List Tree Structure.
// 单链表树结构。
child: Fiber | null,
sibling: Fiber | null,
index: number,
// The ref last used to attach this node.
// 最后用于附加此节点的 ref。
// 我将避免为 prod 和 model 添加一个 owner 字段作为函数。
// I'll avoid adding an owner field for prod and model that as functions.
ref: null | (((handle: mixed) => void) & {_stringRef: ?string}) | RefObject,
// Input is the data coming into process this fiber. Arguments. Props.
// 输入是处理该 fiber 的数据。Arguments. Props.
pendingProps: any, // This type will be more specific once we overload the tag.一旦我们重载了标签,这种类型将会更加具体。
memoizedProps: any, // The props used to create the output.用于创建输出的 props。
// A queue of state updates and callbacks.
// 状态更新和回调的队列。
updateQueue: UpdateQueue<any> | null,
// The state used to create the output
// 用于创建输出的状态
memoizedState: any,
// A linked-list of contexts that this fiber depends on
// 此 fiber 所依赖的上下文的链表
contextDependencies: ContextDependencyList | null,
// Bitfield that describes properties about the fiber and its subtree. E.g.
// the ConcurrentMode flag indicates whether the subtree should be async-by-
// default. When a fiber is created, it inherits the mode of its
// parent. Additional flags can be set at creation time, but after that the
// value should remain unchanged throughout the fiber's lifetime, particularly
// before its child fibers are created.
// 位字段,描述 fiber 及其子树的属性。
// 例如,ConcurrentMode 标志指示子树是否默认为 async。
// 当创建一个 fiber 时,它继承其父的 mode 。
// 可以在创建时设置附加标志,但在此之后,值应该在整个 fiber 的生命周期内保持不变,特别是在创建子 fiber 之前。
mode: TypeOfMode,
// Effect
effectTag: SideEffectTag,
// Singly linked list fast path to the next fiber with side-effects.
// 单链表快速路径到下一个具有副作用的 fiber。
nextEffect: Fiber | null,
// The first and last fiber with side-effect within this subtree. This allows
// us to reuse a slice of the linked list when we reuse the work done within
// this fiber.
// 这个子树中的第一个和最后一个有副作用的 fiber。
// 这允许我们在重用在这个 fiber 中完成的工作时重用链表的一部分。
firstEffect: Fiber | null,
lastEffect: Fiber | null,
// Represents a time in the future by which this work should be completed.
// Does not include work found in its subtree.
// 表示将来完成这项工作的时间。
// 不包括子树中的工作。
expirationTime: ExpirationTime,
// This is used to quickly determine if a subtree has no pending changes.
// 这用于快速确定子树是否没有挂起的更改。
childExpirationTime: ExpirationTime,
// This is a pooled version of a Fiber. Every fiber that gets updated will
// eventually have a pair. There are cases when we can clean up pairs to save
// memory if we need to.
// 这是 fiber 的混合版本。
// 每一个被更新的 fiber 最终都会有一对。
// 在某些情况下,如果需要,我们可以清理它以节省内存。
alternate: Fiber | null,
// Time spent rendering this Fiber and its descendants for the current update.
// This tells us how well the tree makes use of sCU for memoization.
// It is reset to 0 each time we render and only updated when we don't bailout.
// This field is only set when the enableProfilerTimer flag is enabled.
// 为当前更新进行渲染此 fiber 及其后代所花费的时间。
// 这告诉我们这棵树如何很好地利用 sCU 进行记忆。
// 每次渲染时它都被重置为 0,只有在不进行紧急救援(bailout)时才会更新。
// 只有在启用 enableProfilerTimer 标志时才设置此字段。
actualDuration?: number,
// If the Fiber is currently active in the "render" phase,
// This marks the time at which the work began.
// This field is only set when the enableProfilerTimer flag is enabled.
// 如果 fiber 目前处于“渲染”阶段,
// 这标志着这项工作(work)开始的时间。
// 只有在启用 enableProfilerTimer 标志时才设置此字段。
actualStartTime?: number,
// Duration of the most recent render time for this Fiber.
// This value is not updated when we bailout for memoization purposes.
// This field is only set when the enableProfilerTimer flag is enabled.
// 此 fiber 最近渲染的持续时间。
// 当我们出于记忆目的进行紧急救助(bailout)时,此值不会更新。
// 只有在启用 enableProfilerTimer 标志时才设置此字段。
selfBaseDuration?: number,
// Sum of base times for all descedents of this Fiber.
// This value bubbles up during the "complete" phase.
// This field is only set when the enableProfilerTimer flag is enabled.
// 这个 fiber 的所有 descedents 的基时间之和。
// 这个值在“完成”阶段出现。
// 只有在启用 enableProfilerTimer 标志时才设置此字段。
treeBaseDuration?: number,
// Conceptual 的别名
// workInProgress : Fiber -> alternate
// 用于重用的替代方法恰好与正在进行的 work 相同。
// 对于复用的替代方法恰好与正在进行的 work 相同。
// The alternate used for reuse happens
// to be the same as work in progress.
// __DEV__ only
_debugID?: number,
_debugSource?: Source | null,
_debugOwner?: Fiber | null,
_debugIsCurrentlyTiming?: boolean,
// Used to verify that the order of hooks does not change between renders.
// 用于验证钩子的顺序在两次渲染之间没有发生变化。
_debugHookTypes?: Array<HookType> | null,
|};
复制代码
FiberNode
function FiberNode(
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
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.contextDependencies = null;
this.mode = mode;
// Effects
this.effectTag = NoEffect;
this.nextEffect = null;
this.firstEffect = null;
this.lastEffect = null;
this.expirationTime = NoWork;
this.childExpirationTime = NoWork;
this.alternate = null;
if (enableProfilerTimer) {
// 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;
}
if (__DEV__) {
// do something
}
}
复制代码
createFiber
// 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.
const createFiber = function(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
mode: TypeOfMode,
): Fiber {
// $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
return new FiberNode(tag, pendingProps, key, mode);
};
复制代码
createWorkInProgress
// This is used to create an alternate fiber to do work on.
// 用于创建进行工作的备用 fiber。
export function createWorkInProgress(
current: Fiber,
pendingProps: any,
expirationTime: ExpirationTime,
): 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._debugID = current._debugID;
workInProgress._debugSource = current._debugSource;
workInProgress._debugOwner = current._debugOwner;
workInProgress._debugHookTypes = current._debugHookTypes;
}
workInProgress.alternate = current;
current.alternate = workInProgress;
} else {
workInProgress.pendingProps = pendingProps;
// 我们已经有一个 alternate.
// 重置效果标签。
workInProgress.effectTag = NoEffect;
// The effect list is no longer valid.
// 效果列表不再有效。
workInProgress.nextEffect = null;
workInProgress.firstEffect = null;
workInProgress.lastEffect = 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.
workInProgress.actualDuration = 0;
workInProgress.actualStartTime = -1;
}
}
workInProgress.childExpirationTime = current.childExpirationTime;
workInProgress.expirationTime = current.expirationTime;
workInProgress.child = current.child;
workInProgress.memoizedProps = current.memoizedProps;
workInProgress.memoizedState = current.memoizedState;
workInProgress.updateQueue = current.updateQueue;
workInProgress.contextDependencies = current.contextDependencies;
// These will be overridden during the parent's reconciliation
workInProgress.sibling = current.sibling;
workInProgress.index = current.index;
workInProgress.ref = current.ref;
if (enableProfilerTimer) {
workInProgress.selfBaseDuration = current.selfBaseDuration;
workInProgress.treeBaseDuration = current.treeBaseDuration;
}
return workInProgress;
}
复制代码
createHostRootFiber
export function createHostRootFiber(isConcurrent: boolean): Fiber {
let mode = isConcurrent ? ConcurrentMode | StrictMode : NoContext;
if (enableProfilerTimer && isDevToolsPresent) {
// Always collect profile timings when DevTools are present.
// This enables DevTools to start capturing timing at any point–
// Without some nodes in the tree having empty base times.
mode |= ProfileMode;
}
return createFiber(HostRoot, null, null, mode);
}
复制代码
createFiberFromTypeAndProps
/**
* 根据 type 和 props 创建 fiber
* @param type
* @param key
* @param pendingProps
* @param owner
* @param mode
* @param expirationTime
* @returns {Fiber|*}
*/
export function createFiberFromTypeAndProps(
type: any, // React$ElementType
key: null | string,
pendingProps: any,
owner: null | Fiber,
mode: TypeOfMode,
expirationTime: ExpirationTime,
): Fiber {
let fiber;
let fiberTag = IndeterminateComponent;
// The resolved type is set if we know what the final type will be. I.e. it's not lazy.
let resolvedType = type;
if (typeof type === 'function') {
if (shouldConstruct(type)) {
fiberTag = ClassComponent;
}
} else if (typeof type === 'string') {
fiberTag = HostComponent;
} else {
getTag: switch (type) {
case REACT_FRAGMENT_TYPE:
return createFiberFromFragment(
pendingProps.children,
mode,
expirationTime,
key,
);
case REACT_CONCURRENT_MODE_TYPE:
return createFiberFromMode(
pendingProps,
mode | ConcurrentMode | StrictMode,
expirationTime,
key,
);
case REACT_STRICT_MODE_TYPE:
return createFiberFromMode(
pendingProps,
mode | StrictMode,
expirationTime,
key,
);
case REACT_PROFILER_TYPE:
return createFiberFromProfiler(pendingProps, mode, expirationTime, key);
case REACT_SUSPENSE_TYPE:
return createFiberFromSuspense(pendingProps, mode, expirationTime, key);
default: {
if (typeof type === 'object' && type !== null) {
switch (type.$$typeof) {
case REACT_PROVIDER_TYPE:
fiberTag = ContextProvider;
break getTag;
case REACT_CONTEXT_TYPE:
// This is a consumer
fiberTag = ContextConsumer;
break getTag;
case REACT_FORWARD_REF_TYPE:
fiberTag = ForwardRef;
break getTag;
case REACT_MEMO_TYPE:
fiberTag = MemoComponent;
break getTag;
case REACT_LAZY_TYPE:
fiberTag = LazyComponent;
resolvedType = null;
break getTag;
}
}
let info = '';
if (__DEV__) {
if (
type === undefined ||
(typeof type === 'object' &&
type !== null &&
Object.keys(type).length === 0)
) {
info +=
' You likely forgot to export your component from the file ' +
"it's defined in, or you might have mixed up default and " +
'named imports.';
}
const ownerName = owner ? getComponentName(owner.type) : null;
if (ownerName) {
info += '\n\nCheck the render method of `' + ownerName + '`.';
}
}
invariant(
false,
'Element type is invalid: expected a string (for built-in ' +
'components) or a class/function (for composite components) ' +
'but got: %s.%s',
type == null ? type : typeof type,
info,
);
}
}
}
fiber = createFiber(fiberTag, pendingProps, key, mode);
fiber.elementType = type;
fiber.type = resolvedType;
fiber.expirationTime = expirationTime;
return fiber;
}
复制代码
createFiberFromElement
/**
* 根据 element 创建 fiber
* @param element
* @param mode
* @param expirationTime
* @returns {Fiber}
*/
export function createFiberFromElement(
element: ReactElement,
mode: TypeOfMode,
expirationTime: ExpirationTime,
): Fiber {
let owner = null;
if (__DEV__) {
owner = element._owner;
}
const type = element.type;
const key = element.key;
const pendingProps = element.props;
const fiber = createFiberFromTypeAndProps(
type,
key,
pendingProps,
owner,
mode,
expirationTime,
);
if (__DEV__) {
fiber._debugSource = element._source;
fiber._debugOwner = element._owner;
}
return fiber;
}
复制代码
createFiberFromFragment
/**
* 根据 Fragment 创建 Fiber
* @param elements
* @param mode
* @param expirationTime
* @param key
* @returns {Fiber}
*/
export function createFiberFromFragment(
elements: ReactFragment,
mode: TypeOfMode,
expirationTime: ExpirationTime,
key: null | string,
): Fiber {
const fiber = createFiber(Fragment, elements, key, mode);
fiber.expirationTime = expirationTime;
return fiber;
}
复制代码
createFiberFromProfiler
/**
* 根据 Profiler 创建 Fiber
* @param pendingProps
* @param mode
* @param expirationTime
* @param key
* @returns {Fiber}
*/
function createFiberFromProfiler(
pendingProps: any,
mode: TypeOfMode,
expirationTime: ExpirationTime,
key: null | string,
): Fiber {
if (__DEV__) {
if (
typeof pendingProps.id !== 'string' ||
typeof pendingProps.onRender !== 'function'
) {
warningWithoutStack(
false,
'Profiler must specify an "id" string and "onRender" function as props',
);
}
}
const fiber = createFiber(Profiler, pendingProps, key, mode | ProfileMode);
// TODO: The Profiler fiber shouldn't have a type. It has a tag.
fiber.elementType = REACT_PROFILER_TYPE;
fiber.type = REACT_PROFILER_TYPE;
fiber.expirationTime = expirationTime;
return fiber;
}
复制代码
createFiberFromMode
/**
* 根据 mode 创建 fiber
* @param pendingProps
* @param mode
* @param expirationTime
* @param key
* @returns {Fiber}
*/
function createFiberFromMode(
pendingProps: any,
mode: TypeOfMode,
expirationTime: ExpirationTime,
key: null | string,
): Fiber {
const fiber = createFiber(Mode, pendingProps, key, mode);
// TODO: The Mode fiber shouldn't have a type. It has a tag.
const type =
(mode & ConcurrentMode) === NoContext
? REACT_STRICT_MODE_TYPE
: REACT_CONCURRENT_MODE_TYPE;
fiber.elementType = type;
fiber.type = type;
fiber.expirationTime = expirationTime;
return fiber;
}
复制代码
createFiberFromSuspense
/**
* 根据 Suspense 创建 fiber
* @param pendingProps
* @param mode
* @param expirationTime
* @param key
* @returns {Fiber}
*/
export function createFiberFromSuspense(
pendingProps: any,
mode: TypeOfMode,
expirationTime: ExpirationTime,
key: null | string,
) {
const fiber = createFiber(SuspenseComponent, pendingProps, key, mode);
// TODO: The SuspenseComponent fiber shouldn't have a type. It has a tag.
const type = REACT_SUSPENSE_TYPE;
fiber.elementType = type;
fiber.type = type;
fiber.expirationTime = expirationTime;
return fiber;
}
复制代码
createFiberFromText
/**
* 基于文本创建 fiber
* @param content
* @param mode
* @param expirationTime
* @returns {Fiber}
*/
export function createFiberFromText(
content: string,
mode: TypeOfMode,
expirationTime: ExpirationTime,
): Fiber {
const fiber = createFiber(HostText, content, null, mode);
fiber.expirationTime = expirationTime;
return fiber;
}
复制代码
createFiberFromHostInstanceForDeletion
export function createFiberFromHostInstanceForDeletion(): Fiber {
const fiber = createFiber(HostComponent, null, null, NoContext);
// TODO: These should not need a type.
fiber.elementType = 'DELETED';
fiber.type = 'DELETED';
return fiber;
}
复制代码
createFiberFromPortal
/**
* 创建来自 portal 的fiber
* @param portal
* @param mode
* @param expirationTime
* @returns {Fiber}
*/
export function createFiberFromPortal(
portal: ReactPortal,
mode: TypeOfMode,
expirationTime: ExpirationTime,
): Fiber {
const pendingProps = portal.children !== null ? portal.children : [];
const fiber = createFiber(HostPortal, pendingProps, portal.key, mode);
fiber.expirationTime = expirationTime;
fiber.stateNode = {
containerInfo: portal.containerInfo,
pendingChildren: null, // Used by persistent updates
implementation: portal.implementation,
};
return fiber;
}
复制代码