performWorkOnRoot 中的 renderRoot 生成 React Tree Reconciliation,completeRoot 则是 commit 阶段。
例如,同步更新代码如下:
function performWorkOnRoot(
root: FiberRoot,
expirationTime: ExpirationTime,
isYieldy: boolean,
) {
//...
renderRoot(root, isYieldy);
finishedWork = root.finishedWork;
if (finishedWork !== null) {
// We've completed the root. Commit it.
completeRoot(root, finishedWork, expirationTime);
}
//...
}
renderRoot之后,就开始completeRoot。此时的执行栈为:
commitRoot进入提交阶段。在completeRoot中可以阻止 commit 提交!
function completeRoot(
root: FiberRoot,finishedWork: Fiber, expirationTime: ExpirationTime
): void {
const firstBatch = root.firstBatch;
if (firstBatch !== null && firstBatch._expirationTime >= expirationTime) {
if (completedBatches === null) {
completedBatches = [firstBatch];
} else {
completedBatches.push(firstBatch);
}
if (firstBatch._defer) {// 阻止提交
root.finishedWork = finishedWork;
root.expirationTime = NoWork;
return;
}
}
root.finishedWork = null;
if (root === lastCommittedRootDuringThisBatch) {//嵌套循环计数
nestedUpdateCount++;
} else {
lastCommittedRootDuringThisBatch = root;
nestedUpdateCount = 0;
}
commitRoot(root, finishedWork);
}
commitRoot
commitRoot分为commitBeforeMutationLifecycles 、commitAllHostEffects 、commitAllLifeCycles 三个阶段。
function commitRoot(root: FiberRoot, finishedWork: Fiber): void {
isWorking = true;
isCommitting = true;
startCommitTimer();
const committedExpirationTime = root.pendingCommitExpirationTime;
root.pendingCommitExpirationTime = NoWork;
const updateExpirationTimeBeforeCommit = finishedWork.expirationTime;
const childExpirationTimeBeforeCommit = finishedWork.childExpirationTime;
const earliestRemainingTimeBeforeCommit =
childExpirationTimeBeforeCommit > updateExpirationTimeBeforeCommit
? childExpirationTimeBeforeCommit : updateExpirationTimeBeforeCommit;
markCommittedPriorityLevels(root, earliestRemainingTimeBeforeCommit);
let prevInteractions: Set<Interaction> = (null: any);
if (enableSchedulerTracing) {
prevInteractions = __interactionsRef.current;
__interactionsRef.current = root.memoizedInteractions;
}
ReactCurrentOwner.current = null;
let firstEffect;
if (finishedWork.effectTag > PerformedWork) {
if (finishedWork.lastEffect !== null) {
finishedWork.lastEffect.nextEffect = finishedWork;
firstEffect = finishedWork.firstEffect;
} else {
firstEffect = finishedWork;
}
} else {
firstEffect = finishedWork.firstEffect;
}
prepareForCommit(root.containerInfo);
// Invoke instances of getSnapshotBeforeUpdate before mutation.
nextEffect = firstEffect;
startCommitSnapshotEffectsTimer();
while (nextEffect !== null) {
let didError = false;
let error;
try {
commitBeforeMutationLifecycles();
} catch (e) {
didError = true;
error = e;
}
if (didError) {
captureCommitPhaseError(nextEffect, error);
if (nextEffect !== null) {// Clean-up
nextEffect = nextEffect.nextEffect;
}
}
}
stopCommitSnapshotEffectsTimer();
if (enableProfilerTimer) {
recordCommitTime();
}
nextEffect = firstEffect;
startCommitHostEffectsTimer();
while (nextEffect !== null) {
let didError = false;
let error;
try {
commitAllHostEffects();
} catch (e) {
didError = true;
error = e;
}
if (didError) {
captureCommitPhaseError(nextEffect, error);
if (nextEffect !== null) {// Clean-up
nextEffect = nextEffect.nextEffect;
}
}
}
stopCommitHostEffectsTimer();
resetAfterCommit(root.containerInfo);
root.current = finishedWork;
nextEffect = firstEffect;
startCommitLifeCyclesTimer();
while (nextEffect !== null) {
let didError = false;
let error;
try {
commitAllLifeCycles(root, committedExpirationTime);
} catch (e) {
didError = true;
error = e;
}
if (didError) {
captureCommitPhaseError(nextEffect, error);
if (nextEffect !== null) {
nextEffect = nextEffect.nextEffect;
}
}
}
isCommitting = false;
isWorking = false;
stopCommitLifeCyclesTimer();
stopCommitTimer();
onCommitRoot(finishedWork.stateNode);
const updateExpirationTimeAfterCommit = finishedWork.expirationTime;
const childExpirationTimeAfterCommit = finishedWork.childExpirationTime;
const earliestRemainingTimeAfterCommit =
childExpirationTimeAfterCommit > updateExpirationTimeAfterCommit
? childExpirationTimeAfterCommit
: updateExpirationTimeAfterCommit;
if (earliestRemainingTimeAfterCommit === NoWork) {
legacyErrorBoundariesThatAlreadyFailed = null;
}
onCommit(root, earliestRemainingTimeAfterCommit);//清空值
//....
}
执行这三个阶段使用的都是effect链,只做对有此效果的组件进行更新。
commitBeforeMutationLifecycles
commitBeforeMutationLifecycles 执行在mutation 之前的getSnapshotBeforeUpdate 生命周期方法;
//packages\react-reconciler\src\ReactFiberScheduler.js
function commitBeforeMutationLifecycles() {//结尾是 Lifecycles
while (nextEffect !== null) {
const effectTag = nextEffect.effectTag;
if (effectTag & Snapshot) {//检验效果
recordEffect();
const current = nextEffect.alternate;
commitBeforeMutationLifeCycles(current, nextEffect);// 结尾是Lifecycles
}
nextEffect = nextEffect.nextEffect;
}
}
//名字相同,大小写不同
function commitBeforeMutationLifeCycles(current: Fiber | null,finishedWork: Fiber): void {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
commitHookEffectList(UnmountSnapshot, NoHookEffect, finishedWork);
return;
}
case ClassComponent: {
if (finishedWork.effectTag & Snapshot) {
if (current !== null) {
const prevProps = current.memoizedProps;
const prevState = current.memoizedState;
startPhaseTimer(finishedWork, 'getSnapshotBeforeUpdate');
const instance = finishedWork.stateNode;
const snapshot = instance.getSnapshotBeforeUpdate(
finishedWork.elementType === finishedWork.type
? prevProps : resolveDefaultProps(finishedWork.type, prevProps),
prevState,
);
instance.__reactInternalSnapshotBeforeUpdate = snapshot;
stopPhaseTimer();
}
}
return;
}
case HostRoot:
case HostComponent:
case HostText:
case HostPortal:
case IncompleteClassComponent:
// Nothing to do for these component types
return;
default: {
invariant(
false,
'This unit of work tag should not have side-effects. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
}
}
commitAllHostEffects
commitAllHostEffects 中将 staticNode 添加到容器中;
function commitAllHostEffects() {
while (nextEffect !== null) {
recordEffect();
const effectTag = nextEffect.effectTag;
if (effectTag & ContentReset) {
commitResetTextContent(nextEffect);//清空文本
}
if (effectTag & Ref) {
const current = nextEffect.alternate;
if (current !== null) {
commitDetachRef(current);
}
}
let primaryEffectTag = effectTag & (Placement | Update | Deletion);
switch (primaryEffectTag) {
case Placement: {
commitPlacement(nextEffect);//挂载
nextEffect.effectTag &= ~Placement;
break;
}
case PlacementAndUpdate: {
commitPlacement(nextEffect);
nextEffect.effectTag &= ~Placement;
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
case Update: {
const current = nextEffect.alternate;
commitWork(current, nextEffect);
break;
}
case Deletion: {
commitDeletion(nextEffect);
break;
}
}
nextEffect = nextEffect.nextEffect;
}
}
function commitDetachRef(current: Fiber) {
const currentRef = current.ref;
if (currentRef !== null) {
if (typeof currentRef === 'function') {
currentRef(null);
} else {
currentRef.current = null;
}
}
}
commitPlacement
在 commitPlacement 中查找父元素,并将子元素追加到容器中。
function commitPlacement(finishedWork: Fiber): void {
if (!supportsMutation) { return; }
//递归地将所有主机节点插入父节点
const parentFiber = getHostParentFiber(finishedWork);
// 注意:这两个变量*必须*总是一起更新
let parent;
let isContainer;
switch (parentFiber.tag) {
case HostComponent:
parent = parentFiber.stateNode;
isContainer = false;
break;
case HostRoot:
parent = parentFiber.stateNode.containerInfo;
isContainer = true;
break;
case HostPortal:
parent = parentFiber.stateNode.containerInfo;
isContainer = true;
break;
default:
invariant(false, 'Invalid host parent fiber. This error is likely caused by a bug ' +
'in React. Please file an issue.' );
}
if (parentFiber.effectTag & ContentReset) {
// 在执行任何插入之前重置父类的文本内容
resetTextContent(parent);
// 从效果标签中清除ContentReset
parentFiber.effectTag &= ~ContentReset;
}
const before = getHostSibling(finishedWork);
let node: Fiber = finishedWork;
while (true) {
if (node.tag === HostComponent || node.tag === HostText) {
if (before) {
if (isContainer) {
insertInContainerBefore(parent, node.stateNode, before);
} else {
insertBefore(parent, node.stateNode, before);
}
} else {
if (isContainer) {// 将子节点追加到容器中
appendChildToContainer(parent, node.stateNode);
} else {
appendChild(parent, node.stateNode);
}
}
} else if (node.tag === HostPortal) {
} else if (node.child !== null) {
node.child.return = node;
node = node.child;
continue;
}
if (node === finishedWork) { return; }
while (node.sibling === null) {
if (node.return === null || node.return === finishedWork) { return; }
node = node.return;
}
node.sibling.return = node.return;
node = node.sibling;
}
}
在commitPlacement 中实现第一次渲染:浏览器中可见到dom树。
commitWork
最主要更新的是HostComponent、HostText、SuspenseComponent。HostComponent 更新时会参照UpdatePayload(finishedWork.updateQueue) 来进行更新。
function commitWork(current: Fiber | null, finishedWork: Fiber): void {
if (!supportsMutation) {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent: {
commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
return;
}
}
commitContainer(finishedWork);
return;
}
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case MemoComponent:
case SimpleMemoComponent: {
//注意:我们目前从不使用MountMutation,但是useLayout使用UnmountMutation.
commitHookEffectList(UnmountMutation, MountMutation, finishedWork);
return;
}
case ClassComponent: {
return; }
case HostComponent: {
const instance: Instance = finishedWork.stateNode;
if (instance != null) {// 提前完成准备好的工作.
const newProps = finishedWork.memoizedProps;
const oldProps = current !== null ? current.memoizedProps : newProps;
const type = finishedWork.type;
const updatePayload: null | UpdatePayload = (finishedWork.updateQueue: any);
finishedWork.updateQueue = null;
if (updatePayload !== null) {
commitUpdate(instance,updatePayload,type,oldProps,newProps,finishedWork);
}
}
return;
}
case HostText: {
const textInstance: TextInstance = finishedWork.stateNode;
const newText: string = finishedWork.memoizedProps;
const oldText: string = current !== null ? current.memoizedProps : newText;
commitTextUpdate(textInstance, oldText, newText);
return;
}
case HostRoot: { return; }
case Profiler: { return; }
case SuspenseComponent: {
let newState: SuspenseState | null = finishedWork.memoizedState;
let newDidTimeout;
let primaryChildParent = finishedWork;
if (newState === null) {
newDidTimeout = false;
} else {
newDidTimeout = true;
primaryChildParent = finishedWork.child;
if (newState.timedOutAt === NoWork) {
newState.timedOutAt = requestCurrentTime();
}
}
if (primaryChildParent !== null) {
hideOrUnhideAllChildren(primaryChildParent, newDidTimeout);
}
// 如果这个边界刚好超时,那么它将有一组thenable。
// 对于每个thenable,附加一个侦听器,以便当它解析时,React尝试以主(预超时)状态重新呈现边界。
const thenables: Set<Thenable> | null = (finishedWork.updateQueue: any);
if (thenables !== null) {
finishedWork.updateQueue = null;
let retryCache = finishedWork.stateNode;
if (retryCache === null) {
retryCache = finishedWork.stateNode = new PossiblyWeakSet();
}
thenables.forEach(thenable => {
// Memoize using the boundary fiber to prevent redundant listeners.
let retry = retryTimedOutBoundary.bind(null, finishedWork, thenable);
if (enableSchedulerTracing) {
retry = Schedule_tracing_wrap(retry);
}
if (!retryCache.has(thenable)) {
retryCache.add(thenable);
thenable.then(retry, retry);
}
});
}
return;
}
case IncompleteClassComponent: {
return;
}
default: {
invariant(
false,
'This unit of work tag should not have side-effects. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
}
}
react会保留dom对象的引用,更新HostComponent、HostText 时直接使用对应的引用,完成具体 dom 或文本的更新。commitAllHostEffects 在一个大的循环中进行的,更新所有的元素。
commitAllLifeCycles
挂载或更新完成后,进入到commitAllLifeCycles 阶段。commitAllLifeCycles 执行componentDidMount或componentDidUpdate。
function commitAllLifeCycles(finishedRoot: FiberRoot,committedExpirationTime: ExpirationTime) {
while (nextEffect !== null) {
const effectTag = nextEffect.effectTag;
if (effectTag & (Update | Callback)) {
recordEffect();
const current = nextEffect.alternate;
commitLifeCycles(finishedRoot,current,nextEffect,committedExpirationTime);
}
if (effectTag & Ref) {
recordEffect();
commitAttachRef(nextEffect);
}
if (enableHooks && effectTag & Passive) {
rootWithPendingPassiveEffects = finishedRoot;
}
nextEffect = nextEffect.nextEffect;
}
}
commitLifeCycles
执行相应组件的生命周期。
function commitLifeCycles(finishedRoot: FiberRoot, current: Fiber | null,finishedWork: Fiber,
committedExpirationTime: ExpirationTime,
): void {
switch (finishedWork.tag) {
case FunctionComponent:
case ForwardRef:
case SimpleMemoComponent: {
commitHookEffectList(UnmountLayout, MountLayout, finishedWork);
break;
}
case ClassComponent: {
const instance = finishedWork.stateNode;
if (finishedWork.effectTag & Update) {
if (current === null) {
startPhaseTimer(finishedWork, 'componentDidMount');
instance.componentDidMount();
stopPhaseTimer();
} else {
const prevProps =
finishedWork.elementType === finishedWork.type
? current.memoizedProps
: resolveDefaultProps(finishedWork.type, current.memoizedProps);
const prevState = current.memoizedState;
startPhaseTimer(finishedWork, 'componentDidUpdate');
instance.componentDidUpdate(prevProps,prevState,
instance.__reactInternalSnapshotBeforeUpdate);
stopPhaseTimer();
}
}
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
commitUpdateQueue(finishedWork,updateQueue,instance,committedExpirationTime);
}
return;
}
case HostRoot: {
const updateQueue = finishedWork.updateQueue;
if (updateQueue !== null) {
let instance = null;
if (finishedWork.child !== null) {
switch (finishedWork.child.tag) {
case HostComponent:
instance = getPublicInstance(finishedWork.child.stateNode);
break;
case ClassComponent:
instance = finishedWork.child.stateNode;
break;
}
}
commitUpdateQueue(finishedWork,updateQueue,instance,committedExpirationTime);
}
return;
}
case HostComponent: {
const instance: Instance = finishedWork.stateNode;
if (current === null && finishedWork.effectTag & Update) {
const type = finishedWork.type;
const props = finishedWork.memoizedProps;
commitMount(instance, type, props, finishedWork);
}
return;
}
case HostText: {
return;
}
case HostPortal: {
return;
}
case Profiler: {
if (enableProfilerTimer) {
const onRender = finishedWork.memoizedProps.onRender;
if (enableSchedulerTracing) {
onRender(finishedWork.memoizedProps.id,
current === null ? 'mount' : 'update',
finishedWork.actualDuration,
finishedWork.treeBaseDuration,
finishedWork.actualStartTime,
getCommitTime(),
finishedRoot.memoizedInteractions,
);
} else {
onRender(
finishedWork.memoizedProps.id,
current === null ? 'mount' : 'update',
finishedWork.actualDuration,
finishedWork.treeBaseDuration,
finishedWork.actualStartTime,
getCommitTime(),
);
}
}
return;
}
case SuspenseComponent:
break;
case IncompleteClassComponent:
break;
default: {
invariant(
false,
'This unit of work tag should not have side-effects. This error is ' +
'likely caused by a bug in React. Please file an issue.',
);
}
}
}
我们常用的是ClassComponent中componentDidMount、componentDidUpdate 生命周期,如果在组件生命周期中调用setState,则会触发二次渲染。至此组件挂载和生命周期基本完成。