执行完 render()阶段后,将回到 performSyncWorkOnRoot,执行底部的 commitRoot()
function performSyncWorkOnRoot(root) {
if (!((executionContext & (RenderContext | CommitContext)) === NoContext)) {
{
throw Error('Should not already be working.')
}
}
flushPassiveEffects()
var lanes
var exitStatus
if (
root === workInProgressRoot &&
includesSomeLane(root.expiredLanes, workInProgressRootRenderLanes)
) {
// There's a partial tree, and at least one of its lanes has expired. Finish
// rendering it before rendering the rest of the expired work.
lanes = workInProgressRootRenderLanes
exitStatus = renderRootSync(root, lanes)
if (
includesSomeLane(
workInProgressRootIncludedLanes,
workInProgressRootUpdatedLanes,
)
) {
// The render included lanes that were updated during the render phase.
// For example, when unhiding a hidden tree, we include all the lanes
// that were previously skipped when the tree was hidden. That set of
// lanes is a superset of the lanes we started rendering with.
//
// Note that this only happens when part of the tree is rendered
// concurrently. If the whole tree is rendered synchronously, then there
// are no interleaved events.
lanes = getNextLanes(root, lanes)
exitStatus = renderRootSync(root, lanes)
}
} else {
lanes = getNextLanes(root, NoLanes)
exitStatus = renderRootSync(root, lanes)
}
if (root.tag !== LegacyRoot && exitStatus === RootErrored) {
executionContext |= RetryAfterError // If an error occurred during hydration,
// discard server response and fall back to client side render.
if (root.hydrate) {
root.hydrate = false
clearContainer(root.containerInfo)
} // If something threw an error, try rendering one more time. We'll render
// synchronously to block concurrent data mutations, and we'll includes
// all pending updates are included. If it still fails after the second
// attempt, we'll give up and commit the resulting tree.
lanes = getLanesToRetrySynchronouslyOnError(root)
if (lanes !== NoLanes) {
exitStatus = renderRootSync(root, lanes)
}
}
if (exitStatus === RootFatalErrored) {
var fatalError = workInProgressRootFatalError
prepareFreshStack(root, NoLanes)
markRootSuspended$1(root, lanes)
ensureRootIsScheduled(root, now())
throw fatalError
} // We now have a consistent tree. Because this is a sync render, we
// will commit it even if something suspended.
var finishedWork = root.current.alternate
root.finishedWork = finishedWork
root.finishedLanes = lanes
// 进入commit()阶段
commitRoot(root) // Before exiting, make sure there's a callback scheduled for the next
// pending level.
ensureRootIsScheduled(root, now())
return null
}
执行 render()阶段时,会将标记了 flags 的节点用链表链接
function completeUnitOfWork(unitOfWork) {
// Attempt to complete the current unit of work, then move to the next
// sibling. If there are no more siblings, return to the parent fiber.
var completedWork = unitOfWork
do {
// The current, flushed, state of this fiber is the alternate. Ideally
// nothing should rely on this, but relying on it here means that we don't
// need an additional field on the work in progress.
var current = completedWork.alternate
var returnFiber = completedWork.return // Check if the work completed or if something threw.
if ((completedWork.flags & Incomplete) === NoFlags) {
setCurrentFiber(completedWork)
var next = void 0
if ((completedWork.mode & ProfileMode) === NoMode) {
next = completeWork(current, completedWork, subtreeRenderLanes)
} else {
startProfilerTimer(completedWork)
next = completeWork(current, completedWork, subtreeRenderLanes) // Update render duration assuming we didn't error.
stopProfilerTimerIfRunningAndRecordDelta(completedWork, false)
}
resetCurrentFiber()
if (next !== null) {
// Completing this fiber spawned new work. Work on that next.
workInProgress = next
return
}
resetChildLanes(completedWork)
// ---
// 根的firstEffect指向第一个含effectTag的节点
// 含effectTag的节点的nextEffect指向最后一个
// 根的lastEffect指向最后含effectTag的节点
// completeWork为当前fiber
// 假设
// <header>
// <p><code></code></p>
// <a></a>
// </header>
// code在completeWork阶段时,returnFiber为p
// 当code的兄弟节点报错时,p会被标记Incomplete,按位&则会有1,不等于NoFlags
if (
returnFiber !== null && // Do not append effects to parents if a sibling failed to complete
(returnFiber.flags & Incomplete) === NoFlags
) {
// p存在且子节点未报错时
// Append all the effects of the subtree and this fiber onto the effect
// list of the parent. The completion order of the children affects the
// side-effect order.
// p.firstEffect=null,header.firstEffect=code,div.firstEffect=code
if (returnFiber.firstEffect === null) {
returnFiber.firstEffect = completedWork.firstEffect
}
// header.lastEffect=code,div.lastEffect=p
if (completedWork.lastEffect !== null) {
if (returnFiber.lastEffect !== null) {
returnFiber.lastEffect.nextEffect = completedWork.firstEffect
}
returnFiber.lastEffect = completedWork.lastEffect
} // If this fiber had side-effects, we append it AFTER the children's
// side-effects. We can perform certain side-effects earlier if needed,
// by doing multiple passes over the effect list. We don't want to
// schedule our own side-effect on our own list because if end up
// reusing children we'll schedule this effect onto itself since we're
// at the end.
var flags = completedWork.flags // Skip both NoWork and PerformedWork tags when creating the effect
// list. PerformedWork effect is read by React DevTools but shouldn't be
// committed.
// 是否有标记
if (flags > PerformedWork) {
// p.lastEffect是否存在
if (returnFiber.lastEffect !== null) {
// 注意 这里是NEXTEffect
// header.lastEffect.nextEffect(code)=p
returnFiber.lastEffect.nextEffect = completedWork
} else {
// p.firstEffect赋值为code
returnFiber.firstEffect = completedWork
}
// p.lastEffect赋值为code,header.lastEffect=p
returnFiber.lastEffect = completedWork
}
// 之后进入p,a(无flags跳过),header,div
}
} else {
// This fiber did not complete because something threw. Pop values off
// the stack without entering the complete phase. If this is a boundary,
// capture values if possible.
var _next = unwindWork(completedWork) // Because this fiber did not complete, don't reset its expiration time.
if (_next !== null) {
// If completing this work spawned new work, do that next. We'll come
// back here again.
// Since we're restarting, remove anything that is not a host effect
// from the effect tag.
_next.flags &= HostEffectMask
workInProgress = _next
return
}
if ((completedWork.mode & ProfileMode) !== NoMode) {
// Record the render duration for the fiber that errored.
stopProfilerTimerIfRunningAndRecordDelta(completedWork, false) // Include the time spent working on failed children before continuing.
var actualDuration = completedWork.actualDuration
var child = completedWork.child
while (child !== null) {
actualDuration += child.actualDuration
child = child.sibling
}
completedWork.actualDuration = actualDuration
}
if (returnFiber !== null) {
// Mark the parent fiber as incomplete and clear its effect list.
returnFiber.firstEffect = returnFiber.lastEffect = null
returnFiber.flags |= Incomplete
}
}
// ---
var siblingFiber = completedWork.sibling
if (siblingFiber !== null) {
// If there is more work to do in this returnFiber, do that next.
workInProgress = siblingFiber
return
} // Otherwise, return to the parent
completedWork = returnFiber // Update the next thing we're working on in case something throws.
workInProgress = completedWork
} while (completedWork !== null) // We've reached the root.
if (workInProgressRootExitStatus === RootIncomplete) {
workInProgressRootExitStatus = RootCompleted
}
}