React16源码: React中的completeUnitOfWork的源码实现
// 这个API 是在 workInProgress 的基础上进行的一个操作
function completeUnitOfWork(workInProgress: Fiber): Fiber | null {
// 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.
// 进来首先就是一个 while true 的一个循环
while (true) {
// 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.
// 对于这个循环, 它首先获取current
const current = workInProgress.alternate;
if (__DEV__) {
ReactCurrentFiber.setCurrentFiber(workInProgress);
}
// 然后 获取 returnFiber 和 siblingFiber,也就是它的父节点以及它的兄弟节点
// 这个在后面我们判断是否要返回兄弟节点的时候就会用到
const returnFiber = workInProgress.return;
const siblingFiber = workInProgress.sibling;
// 这里首先一进来就有一个大的判断,它是一个整个方法里面最大的一个判断
// Incomplete 就是这个节点它是出现了错误,然后被捕获的, 并标记这个 sideEffect
// 逻辑与操作来判断某一个属性上面它是否有某一个特性的一个方式
if ((workInProgress.effectTag & Incomplete) === NoEffect) {
if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
// Don't replay if it fails during completion phase.
mayReplayFailedUnitOfWork = false;
}
// This fiber completed.
// Remember we're completing this unit so we can find a boundary if it fails.
nextUnitOfWork = workInProgress;
if (enableProfilerTimer) {
if (workInProgress.mode & ProfileMode) {
startProfilerTimer(workInProgress);
}
nextUnitOfWork = completeWork(
current,
workInProgress,
nextRenderExpirationTime,
);
if (workInProgress.mode & ProfileMode) {
// Update render duration assuming we didn't error.
stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
}
} else {
nextUnitOfWork = completeWork(
current,
workInProgress,
nextRenderExpirationTime,
);
}
if (__DEV__ && replayFailedUnitOfWorkWithInvokeGuardedCallback) {
// We're out of completion phase so replaying is fine now.
mayReplayFailedUnitOfWork = true;
}
stopWorkTimer(workInProgress);
resetChildExpirationTime(workInProgress, nextRenderExpirationTime);
if (__DEV__) {
ReactCurrentFiber.resetCurrentFiber();
}
if (nextUnitOfWork !== null) {
// Completing this fiber spawned new work. Work on that next.
return nextUnitOfWork;
}
// 接下去这个个判断,是要构造一个所有 有 SideEffect 的节点的一个链状的结构
// 这个链状结构最终是用于 commitWork 的时候用来进行对这些有 SideEffect的节点进行 commit 的一个操作,
// 这边它的一个判断条件,returnFiber不等于null,并且returnFiber它不是一个 Incomplete 的一个节点
// 因为对于一个 Incomplete 的节点,它唯一可以具有的一个SideEffect,就是这个节点已经被捕获了
// 因为对于有 Incomplete 错误的节点是不会渲染正常的子节点的
if (
returnFiber !== null &&
// Do not append effects to parents if a sibling failed to complete
(returnFiber.effectTag & Incomplete) === NoEffect
) {
// 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.
// 对于正常的一个情况, 首先要判断一下 returnFiber.firstEffect 是否等于 null
// 符合判断就代表 现在这个 returnFiber 上还没有记录任何它的子节点的有副作用的子节点
// 这个时候, 直接把当前节点的firstEffect赋值给 returnFiber.firstEffect
// 因为它之前是没有任何一个的嘛,我们这边真正要做的是把当前节点的 firstEffect 到 lastEffect的一个链条
// 这个单项链表,给它挂载到它的父节点的同样的一个 firstEffect到lastEffect的单项链表的最后
if (returnFiber.firstEffect === null) {
returnFiber.firstEffect = workInProgress.firstEffect;
}
// 就是下面这一段判断,就是来做这个事情的, 如果returnFiber.lastEffect不等于null,那说明它已经有了
// 那么对于returnFiber上面有记录过别的 SideEffect 的节点之后
// 我们当前节点是挂载到整个 SideEffect 链的最后,就是下面这样,就是把它连到最后的上面
if (workInProgress.lastEffect !== null) {
if (returnFiber.lastEffect !== null) {
returnFiber.lastEffect.nextEffect = workInProgress.firstEffect;
}
// 还要操作如下,因为它的这个(returnFiber.lastEffect)指针目前还指向它原来的那个lastEffect
// 在 赋值 nextEffect之后,它的最后一个就是这个链的最后一个已经变成 workInProgress.lastEffect,所以这边要执行这么一个操作
// 当然这个条件是要建立在 workInProgress.lastEffect 是有值的情况
// 这是把它们各自的firsteffect到lasteffect,这个链给它进行一个串联的过程
returnFiber.lastEffect = workInProgress.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.
// 对于returnFiber来说,当前这个节点也可能是有副作用的,那么这边就接下去就会做这个操作
// 如果当前节点的 effectTag > PerformedWork 的,因为 PerformedWork 是一个给 DEVTool 用的一个 sideEffect
// 对于真正的react更新是没有任何意义的, 所以如果它仅仅只有 PerformedWork ,它就不是一个有效的 SideEffect 的节点
const effectTag = workInProgress.effectTag;
// Skip both NoWork and PerformedWork tags when creating the effect list.
// PerformedWork effect is read by React DevTools but shouldn't be committed.
if (effectTag > PerformedWork) {
// 如果它有 SideEffect,就把当前节点作为父节点的 SideEffect 链的最后一个给它挂载上去
// 或者如果是当前父节点没有任何记录的 SideEffect,它就是第一个
if (returnFiber.lastEffect !== null) {
returnFiber.lastEffect.nextEffect = workInProgress;
} else {
returnFiber.firstEffect = workInProgress;
}
returnFiber.lastEffect = workInProgress;
}
}
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
}
if (siblingFiber !== null) {
// If there is more work to do in this returnFiber, do that next.
return siblingFiber;
} else if (returnFiber !== null) {
// If there's no more work in this returnFiber. Complete the returnFiber.
workInProgress = returnFiber;
continue;
} else {
// We've reached the root.
return null;
}
} else {
if (enableProfilerTimer && workInProgress.mode & ProfileMode) {
// Record the render duration for the fiber that errored.
stopProfilerTimerIfRunningAndRecordDelta(workInProgress, false);
// Include the time spent working on failed children before continuing.
let actualDuration = workInProgress.actualDuration;
let child = workInProgress.child;
while (child !== null) {
actualDuration += child.actualDuration;
child = child.sibling;
}
workInProgress.actualDuration = actualDuration;
}
// 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.
const next = unwindWork(workInProgress, nextRenderExpirationTime);
// Because this fiber did not complete, don't reset its expiration time.
if (workInProgress.effectTag & DidCapture) {
// Restarting an error boundary
stopFailedWorkTimer(workInProgress);
} else {
stopWorkTimer(workInProgress);
}
if (__DEV__) {
ReactCurrentFiber.resetCurrentFiber();
}
if (next !== null) {
stopWorkTimer(workInProgress);
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
}
// 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.effectTag &= HostEffectMask;
return next;
}
if (returnFiber !== null) {
// Mark the parent fiber as incomplete and clear its effect list.
returnFiber.firstEffect = returnFiber.lastEffect = null;
returnFiber.effectTag |= Incomplete;
}
if (__DEV__ && ReactFiberInstrumentation.debugTool) {
ReactFiberInstrumentation.debugTool.onCompleteWork(workInProgress);
}
// 存在 sibling 节点, 注意这边是return,也就是跳出while循环
if (siblingFiber !== null) {
// If there is more work to do in this returnFiber, do that next.
return siblingFiber;
} else if (returnFiber !== null) {
// If there's no more work in this returnFiber. Complete the returnFiber.
// 如果returnFiber不等于null,那么 workInProgress = returnFiber
// 比如对于最上面示例图的 input节点,它的 completeUnitOfWork
// 如果它没有兄弟节点,那么它就继续执行Input的 completeUnitOfWork
// 这个循环是在 completeUnitOfWork 内部进行的一个过程
workInProgress = returnFiber;
continue;
} else {
// 如果 returnFiber 也等于null,那么它直接 return null
// 说明已经到达顶点了,就到达 RootFiber 了,我们的更新过程呢已经完成了
// 只需要在接下去 commitRoot 就可以了
return null;
}
}
}
// Without this explicit null return Flow complains of invalid return type
// TODO Remove the above while(true) loop
// eslint-disable-next-line no-unreachable
return null;
}