简述
react-reconsiler
模块是React中最核心的模块之一,也是最复杂的模块之一。其中包括React Fiber框架、React Hooks等的实现。
本文主要讲述其中React初始化的主要过程。
初始化过程
简单的来看,初始化过程可以分为三个阶段:
- 第一阶段为创建虚拟DOM的根节点直到调用
scheduleWork
进行调度更新作业前; - 第二阶段为
WorkLoop
阶段,该阶段采用深度优先方式遍历整个虚拟DOM,并根据虚拟DOM生成对应的HTML元素挂载到文档中。 - 第三阶段为
CommitWork
阶段,该阶段处理类组件的componentDidMount
以及函数组件的Effect Hooks.
创建虚拟DOM的根节点
调用createContainer
函数创建一个FiberRoot
节点。例如react-dom
模块中FiberRoot
是在createLegacyRoot
函数中创建ReactDOMBlockingRoot
实例时创建的,同时创建第一个FiberNode
根节点(FiberNode
和FiberRoot
是两种不同的数据结构,FiberNode
与React JSX节点一一对应,即虚拟DOM节点)。
根FiberNode节点的WorkTag
类型为HostRoot
。
ReactDOM.render
=> legacyRenderSubtreeIntoContainer
=> legacyCreateRootFromDOMContainer
=> createLegacyRoot
=> new ReactDOMBlockingRoot(...)
=> createContainer
=> createFiberRoot
=> new FiberRootNode
export function createFiberRoot(
containerInfo: any,
tag: RootTag,
hydrate: boolean,
hydrationCallbacks: null | SuspenseHydrationCallbacks,
): FiberRoot {
// 创建FiberRoot
const root: FiberRoot = (new FiberRootNode(containerInfo, tag, hydrate): any);
if (enableSuspenseCallback) {
root.hydrationCallbacks = hydrationCallbacks;
}
// Cyclic construction. This cheats the type system right now because
// stateNode is any.
// 创建第一个FiberNode
const uninitializedFiber = createHostRootFiber(tag);
root.current = uninitializedFiber;
uninitializedFiber.stateNode = root;
// 初始化更新队列
initializeUpdateQueue(uninitializedFiber);
return root;
}
WorkLoop
阶段
React调用updateContainer
函数更新容器。例如react-dom
的legacyRenderSubtreeIntoContainer
函数中:
// Initial mount should not be batched.
unbatchedUpdates(() => {
updateContainer(children, fiberRoot, parentComponent, callback);
});
而updateContainer
函数最终调用workLoopSync
函数实现对虚拟DOM的遍历。workLoopSync
通过循环调用performUnitOfWork
函数来处理WIP(workInProgress
)。
updateContainer
=> scheduleWork
=> performSyncWorkOnRoot
=> workLoopSync
第一个WIP从根节点生成。
function performSyncWorkOnRoot(root) {
// ...
if (root !== workInProgressRoot || expirationTime !== renderExpirationTime) {
prepareFreshStack(root, expirationTime);
startWorkOnPendingInteractions(root, expirationTime);
}
// ...
}
function prepareFreshStack(root, expirationTime) {
// ...
workInProgressRoot = root;
workInProgress = createWorkInProgress(root.current, null);
renderExpirationTime = expirationTime;
workInProgressRootExitStatus = RootIncomplete;
workInProgressRootFatalError = null;
workInProgressRootLatestProcessedExpirationTime = Sync;
workInProgressRootLatestSuspenseTimeout = Sync;
workInProgressRootCanSuspendUsingConfig = null;
workInProgressRootNextUnprocessedUpdateTime = NoWork;
workInProgressRootHasPendingPing = false;
// ...
}
performUnitOfWork
函数处理WIP分为两个过程:
-
beginWork
过程,根据WIP的WorkTag
完成WIP的初始化工作,同时生成对应子FiberNode(即对应WIP)。
switch (workInProgress.tag) {
case IndeterminateComponent: {
return mountIndeterminateComponent(
current,
workInProgress,
workInProgress.type,
renderExpirationTime,
);
}
case LazyComponent: {
const elementType = workInProgress.elementType;
return mountLazyComponent(
current,
workInProgress,
elementType,
updateExpirationTime,
renderExpirationTime,
);
}
case FunctionComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateFunctionComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime,
);
}
case ClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return updateClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime,
);
}
case HostRoot:
return updateHostRoot(current, workInProgress, renderExpirationTime);
case HostComponent:
return updateHostComponent(current, workInProgress, renderExpirationTime);
case HostText:
return updateHostText(current, workInProgress);
case SuspenseComponent:
return updateSuspenseComponent(
current,
workInProgress,
renderExpirationTime,
);
case HostPortal:
return updatePortalComponent(
current,
workInProgress,
renderExpirationTime,
);
case ForwardRef: {
const type = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === type
? unresolvedProps
: resolveDefaultProps(type, unresolvedProps);
return updateForwardRef(
current,
workInProgress,
type,
resolvedProps,
renderExpirationTime,
);
}
case Fragment:
return updateFragment(current, workInProgress, renderExpirationTime);
case Mode:
return updateMode(current, workInProgress, renderExpirationTime);
case Profiler:
return updateProfiler(current, workInProgress, renderExpirationTime);
case ContextProvider:
return updateContextProvider(
current,
workInProgress,
renderExpirationTime,
);
case ContextConsumer:
return updateContextConsumer(
current,
workInProgress,
renderExpirationTime,
);
case MemoComponent: {
const type = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
// Resolve outer props first, then resolve inner props.
let resolvedProps = resolveDefaultProps(type, unresolvedProps);
if (__DEV__) {
if (workInProgress.type !== workInProgress.elementType) {
const outerPropTypes = type.propTypes;
if (outerPropTypes) {
checkPropTypes(
outerPropTypes,
resolvedProps, // Resolved for outer only
'prop',
getComponentName(type),
getCurrentFiberStackInDev,
);
}
}
}
resolvedProps = resolveDefaultProps(type.type, resolvedProps);
return updateMemoComponent(
current,
workInProgress,
type,
resolvedProps,
updateExpirationTime,
renderExpirationTime,
);
}
case SimpleMemoComponent: {
return updateSimpleMemoComponent(
current,
workInProgress,
workInProgress.type,
workInProgress.pendingProps,
updateExpirationTime,
renderExpirationTime,
);
}
case IncompleteClassComponent: {
const Component = workInProgress.type;
const unresolvedProps = workInProgress.pendingProps;
const resolvedProps =
workInProgress.elementType === Component
? unresolvedProps
: resolveDefaultProps(Component, unresolvedProps);
return mountIncompleteClassComponent(
current,
workInProgress,
Component,
resolvedProps,
renderExpirationTime,
);
}
case SuspenseListComponent: {
return updateSuspenseListComponent(
current,
workInProgress,
renderExpirationTime,
);
}
case FundamentalComponent: {
if (enableFundamentalAPI) {
return updateFundamentalComponent(
current,
workInProgress,
renderExpirationTime,
);
}
break;
}
case ScopeComponent: {
if (enableScopeAPI) {
return updateScopeComponent(
current,
workInProgress,
renderExpirationTime,
);
}
break;
}
case Block: {
if (enableBlocksAPI) {
const block = workInProgress.type;
const props = workInProgress.pendingProps;
return updateBlock(
current,
workInProgress,
block,
props,
renderExpirationTime,
);
}
break;
}
}
例如
ClassComponent
类型的WIP将创建对应类实例,调用组件的componentWillMount
钩子函数,执行组件的render
函数。
function updateClassComponent(
current: Fiber | null,
workInProgress: Fiber,
Component: any,
nextProps,
renderExpirationTime: ExpirationTime,
) {
// ...
const instance = workInProgress.stateNode;
let shouldUpdate;
if (instance === null) {
if (current !== null) {
// 创建Class组件实例,并调用componentWillMount。
constructClassInstance(workInProgress, Component, nextProps);
mountClassInstance(
workInProgress,
Component,
nextProps,
renderExpirationTime,
);
shouldUpdate = true;
} else if (current === null) {
// ...
} else {
// ...
}
const nextUnitOfWork = finishClassComponent(
current,
workInProgress,
Component,
shouldUpdate,
hasContext,
renderExpirationTime,
);
// ...
return nextUnitOfWork;
}
function finishClassComponent(
current: Fiber | null,
workInProgress: Fiber,
Component: any,
shouldUpdate: boolean,
hasContext: boolean,
renderExpirationTime: ExpirationTime,
) {
// ...
if (
didCaptureError &&
typeof Component.getDerivedStateFromError !== 'function'
) {
// ...
} else {
if (__DEV__) {
// ...
} else {
// 调用Class组件的render函数,返回其子节点。
nextChildren = instance.render();
}
}
// React DevTools reads this flag.
workInProgress.effectTag |= PerformedWork;
if (current !== null && didCaptureError) {
// ...
} else {
// 调用reconcileChildren生成下一个WIP。
reconcileChildren(
current,
workInProgress,
nextChildren,
renderExpirationTime,
);
}
// ...
return workInProgress.child;
}
-
completeWork
过程,对beginWork
的中处理的一些压栈处理的逆操作。例如pushHostContext
执行popHostContext
操作,pushHostContainer
执行popHostContainer
操作。
此外,最重要的一点是对HostComponent
和HostText
两种类型的WIP分别创建对应的元素,并将子元素插入当前元素中。
case HostComponent: {
// ...
if (wasHydrated) {
// ...
} else {
// 创建对应的元素实例
let instance = createInstance(
type,
newProps,
rootContainerInstance,
currentHostContext,
workInProgress,
);
// 将所有子元素插入到当前元素中。
appendAllChildren(instance, workInProgress, false, false);
// This needs to be set before we mount Flare event listeners
workInProgress.stateNode = instance;
// ...
// Certain renderers require commit-time effects for initial mount.
// (eg DOM renderer supports auto-focus for certain elements).
// Make sure such renderers get scheduled for later work.
if (
finalizeInitialChildren(
instance,
type,
newProps,
rootContainerInstance,
currentHostContext,
)
) {
markUpdate(workInProgress);
}
}
//...
}
return null;
}
case HostText: { // 文本节点
let newText = newProps;
if (current && workInProgress.stateNode != null) {
// ...
} else {
if (typeof newText !== 'string') {
invariant(
workInProgress.stateNode !== null,
'We must have new props for new mounts. This error is likely ' +
'caused by a bug in React. Please file an issue.',
);
// This can happen when we abort work.
}
const rootContainerInstance = getRootHostContainer();
const currentHostContext = getHostContext();
let wasHydrated = popHydrationState(workInProgress);
if (wasHydrated) {
if (prepareToHydrateHostTextInstance(workInProgress)) {
markUpdate(workInProgress);
}
} else {
// 更新对应的文本
workInProgress.stateNode = createTextInstance(
newText,
rootContainerInstance,
currentHostContext,
workInProgress,
);
}
}
return null;
}
commitWork
阶段
在所有节点遍历结束后,初始化过程还将调用finishSyncRender
函数,该函数将调用commitRoot
函数进入commitWork
阶段。
function finishSyncRender(root) {
// Set this to null to indicate there's no in-progress render.
workInProgressRoot = null;
commitRoot(root);
}
在commitWork
阶段,React将完成以下事情:
- 执行Function组件的React Effect Hooks
do {
// ...
// 改方法中将执行Effect Hooks
flushPassiveEffects();
} while (rootWithPendingPassiveEffects !== null);
- 对Class组件调用
componentDidMount
,并计算state
function commitLifeCycles(
finishedRoot: FiberRoot,
current: Fiber | null,
finishedWork: Fiber,
committedExpirationTime: ExpirationTime,
): void {
// ...
case ClassComponent: {
const instance = finishedWork.stateNode;
if (finishedWork.effectTag & Update) {
if (current === null) {
// ...
// 初始化阶段调用`componentDidMount`
instance.componentDidMount();
stopPhaseTimer();
} else {
// ...
// 组件更新阶段调用`componentDidUpdate`
instance.componentDidUpdate(
prevProps,
prevState,
instance.__reactInternalSnapshotBeforeUpdate,
);
stopPhaseTimer();
}
}
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
// We could update instance props and state here,
// but instead we rely on them being set during last render.
// TODO: revisit this when we implement resuming.
commitUpdateQueue(finishedWork, updateQueue, instance);
}
return;
}
}
// ...
}
...等等。
网友评论