赞
踩
1 )概述
commitRoot 这个方法
2 )源码
定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L586
定位到 commitRoot
function commitRoot(root: FiberRoot, finishedWork: Fiber): void { // 一进来,它先设置了两个全局,变量是isworking和iscommitting // isworking,我们在 renderRoot 的过程当中也会设置为 true // 所以说 isWorking 这个全局变量,它就代表着我们正在进行更新的操作, 不管是它在哪一个阶段 // isCommitting 就是单独的指明正在处于commit阶段,即提交的阶段 isWorking = true; isCommitting = true; startCommitTimer(); invariant( root.current !== finishedWork, 'Cannot commit the same tree as before. This is probably a bug ' + 'related to the return field. This error is likely caused by a bug ' + 'in React. Please file an issue.', ); // 进入之后,通过 root.pendingCommitExpirationTime,获取 committedExpirationTime // 这个 pendingCommitExpirationTime 就是在 onCommitRoot 的时候设置的 // 在之前 render 阶段执行操作的 expirationTime const committedExpirationTime = root.pendingCommitExpirationTime; invariant( committedExpirationTime !== NoWork, 'Cannot commit an incomplete root. This error is likely caused by a ' + 'bug in React. Please file an issue.', ); // 设置这个 root.pendingCommitExpirationTime 为 NoWork // 因为即将要对这个 committedExpirationTime 来进行一个commit的操作了 // 接下去就是在执行完之后, root.pendingCommitExpirationTime 肯定就是已经没有了 root.pendingCommitExpirationTime = NoWork; // Update the pending priority levels to account for the work that we are // about to commit. This needs to happen before calling the lifecycles, since // they may schedule additional updates. // 这边会做一个标记,就是 markCommittedPriorityLevels 这是标记我们的一个优先级 // 它标记的结果是根据 finishedWork.expirationTime 以及 finishedWork.childExpirationTime // 因为 finishedWork 是一个 RootFiber,所以它的 childExpirationTime 代表着所有子树当中优先级最高的那一个任务 // 而它的 expirationTime,大部分情况下都应该是跟它的 childExpirationTime 是相同的,但是会有一些情况不一样 // 比如说通过外部强制指定的方式, 比如在有 nextRenderDidError 的情况下 // 会设置它的 experiencetime 等于 Sync 这就是从外部去强制指定它的一个 expirationTime // 在这种情况下,它的 expirationTime 跟它的 childExpirationTime 会有比较大的区别 // 当然并不是说没有这种外部指定,它们一定相同,大部分情况下,它们应该是相同的 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) { // Restore any pending interactions at this point, // So that cascading work triggered during the render phase will be accounted for. prevInteractions = __interactionsRef.current; __interactionsRef.current = root.memoizedInteractions; } // Reset this to null before calling lifecycles ReactCurrentOwner.current = null; // 它判断了 finishedWork,也就是我们的 RootFiber,它的 effectTag 是否大于 PerformedWork // 因为 PerformedWork 是给 DEV Tool 用的, 所以在整体的更新流程当中是没有意义的 // 它大于 PerformedWork 代表它上面有 SideEffect,它也需要被 committed // 所以这边通过这么一个判断,如果是大于 PerformedWork 的了 // 要把 finishedWork 的也作为一个 effect 来增加到它自己的 firstEffect 到 lastEffect 的这个链上面 // 下面是增加的过程,因为要判断它目前上面是否有 SideEffect,以不同的方式插入到整个单链表的最后面 let firstEffect; if (finishedWork.effectTag > PerformedWork) { // A fiber's effect list consists only of its children, not itself. So if // the root has an effect, we need to add it to the end of the list. The // resulting list is the set that would belong to the root's parent, if // it had one; that is, all the effects in the tree including the root. if (finishedWork.lastEffect !== null) { finishedWork.lastEffect.nextEffect = finishedWork; firstEffect = finishedWork.firstEffect; } else { firstEffect = finishedWork; } } else { // There is no effect on the root. firstEffect = finishedWork.firstEffect; } prepareForCommit(root.containerInfo); // Invoke instances of getSnapshotBeforeUpdate before mutation. nextEffect = firstEffect; startCommitSnapshotEffectsTimer(); // 接下去就进入了三个循环: // 第一个循环主要是 commitBeforeMutationLifecycles,这个方法它其实很简单,它是这三个循环当中最简单的一个方法 // 它唯一的作用就是调用 ClassComponent 上面可能会存在的 getSnapshotBeforeUpdate 这么一个生命周期方法 while (nextEffect !== null) { let didError = false; let error; if (__DEV__) { invokeGuardedCallback(null, commitBeforeMutationLifecycles, null); if (hasCaughtError()) { didError = true; error = clearCaughtError(); } } else { try { commitBeforeMutationLifecycles(); } catch (e) { didError = true; error = e; } } if (didError) { invariant( nextEffect !== null, 'Should have next effect. This error is likely caused by a bug ' + 'in React. Please file an issue.', ); captureCommitPhaseError(nextEffect, error); // Clean-up if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; } } } stopCommitSnapshotEffectsTimer(); if (enableProfilerTimer) { // Mark the current commit time to be shared by all Profilers in this batch. // This enables them to be grouped later. recordCommitTime(); } // Commit all the side-effects within a tree. We'll do this in two passes. // The first pass performs all the host insertions, updates, deletions and // ref unmounts. nextEffect = firstEffect; startCommitHostEffectsTimer(); // 第二个循环调用的方法是 commitAllHostEffects // 这个就是主要是操作对于 dom 节点它需要去做的一些内容 // 比如说如果这个 dom 节点是刚刚新增的,那么要执行一个插入的操作 // 如果这个 dom 节点它需要被删除,我们要执行删除的操作 // 如果这个 dom 节点它的属性或者它 children 有更新,那么要执行一个更新的操作 while (nextEffect !== null) { let didError = false; let error; if (__DEV__) { invokeGuardedCallback(null, commitAllHostEffects, null); if (hasCaughtError()) { didError = true; error = clearCaughtError(); } } else { try { commitAllHostEffects(); } catch (e) { didError = true; error = e; } } if (didError) { invariant( nextEffect !== null, 'Should have next effect. This error is likely caused by a bug ' + 'in React. Please file an issue.', ); captureCommitPhaseError(nextEffect, error); // Clean-up if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; } } } stopCommitHostEffectsTimer(); resetAfterCommit(root.containerInfo); // The work-in-progress tree is now the current tree. This must come after // the first pass of the commit phase, so that the previous tree is still // current during componentWillUnmount, but before the second pass, so that // the finished work is current during componentDidMount/Update. root.current = finishedWork; // In the second pass we'll perform all life-cycles and ref callbacks. // Life-cycles happen as a separate pass so that all placements, updates, // and deletions in the entire tree have already been invoked. // This pass also triggers any renderer-specific initial effects. nextEffect = firstEffect; startCommitLifeCyclesTimer(); // 接下去再做第三个方法叫做 commitAllLifeCycles // 这个就是跟组件还有各种各样的所有东西相关的生命周期的方法都会在这里面被调用 while (nextEffect !== null) { let didError = false; let error; if (__DEV__) { invokeGuardedCallback( null, commitAllLifeCycles, null, root, committedExpirationTime, ); if (hasCaughtError()) { didError = true; error = clearCaughtError(); } } else { try { commitAllLifeCycles(root, committedExpirationTime); } catch (e) { didError = true; error = e; } } if (didError) { invariant( nextEffect !== null, 'Should have next effect. This error is likely caused by a bug ' + 'in React. Please file an issue.', ); captureCommitPhaseError(nextEffect, error); if (nextEffect !== null) { nextEffect = nextEffect.nextEffect; } } } if ( enableHooks && firstEffect !== null && rootWithPendingPassiveEffects !== null ) { // This commit included a passive effect. These do not need to fire until // after the next paint. Schedule an callback to fire them in an async // event. To ensure serial execution, the callback will be flushed early if // we enter rootWithPendingPassiveEffects commit phase before then. let callback = commitPassiveEffects.bind(null, root, firstEffect); if (enableSchedulerTracing) { // TODO: Avoid this extra callback by mutating the tracing ref directly, // like we do at the beginning of commitRoot. I've opted not to do that // here because that code is still in flux. callback = Schedule_tracing_wrap(callback); } passiveEffectCallbackHandle = Schedule_scheduleCallback(callback); passiveEffectCallback = callback; } // 以上3个循环中,三个方法调用完之后,我们的commit阶段就算已经完成了 // 然后它会把这些全局变量设置为 false // 以及它会调用一个叫 onCommitRoot 的这么一个方法 isCommitting = false; isWorking = false; stopCommitLifeCyclesTimer(); stopCommitTimer(); onCommitRoot(finishedWork.stateNode); if (__DEV__ && ReactFiberInstrumentation.debugTool) { ReactFiberInstrumentation.debugTool.onCommitWork(finishedWork); } // 它执行一段相关的 expirationTime 的判断, 它为什么要这边再做一次? // 因为我们在执行 ClassComponent 的生命周期方法的过程当中,可能又会产生新的更新 // 产生新的更新的时候,RootFiber的 childExpirationTime 它又有可能会变化 // 它不会把这一部分代码放到最前面去做,而是要放到最后面来做 // 就是因为它需要等到我们如果在生命周期方法里面又创建了新的 update // 又产生了新的 childExpirationTime 的时候,那么再来进行这么一个判断 // 这个判断其实主要做的事情,也就是设置一个全局变量 // 叫做 legacyErrorBoundariesThatAlreadyFailed ,把它设置为 null const updateExpirationTimeAfterCommit = finishedWork.expirationTime; const childExpirationTimeAfterCommit = finishedWork.childExpirationTime; const earliestRemainingTimeAfterCommit = childExpirationTimeAfterCommit > updateExpirationTimeAfterCommit ? childExpirationTimeAfterCommit : updateExpirationTimeAfterCommit; if (earliestRemainingTimeAfterCommit === NoWork) { // If there's no remaining work, we can clear the set of already failed // error boundaries. legacyErrorBoundariesThatAlreadyFailed = null; } // 这里调用了一个方法叫做 onCommit // 这个 earliestRemainingTimeAfterCommit 是优先级较小,值较大的 expirationTime // 它就变成了新的root上面的 expirationTime onCommit(root, earliestRemainingTimeAfterCommit); // 忽略 polyfill 相关的 if (enableSchedulerTracing) { __interactionsRef.current = prevInteractions; let subscriber; try { subscriber = __subscriberRef.current; if (subscriber !== null && root.memoizedInteractions.size > 0) { const threadID = computeThreadID( committedExpirationTime, root.interactionThreadID, ); subscriber.onWorkStopped(root.memoizedInteractions, threadID); } } catch (error) { // It's not safe for commitRoot() to throw. // Store the error for now and we'll re-throw in finishRendering(). if (!hasUnhandledError) { hasUnhandledError = true; unhandledError = error; } } finally { // Clear completed interactions from the pending Map. // Unless the render was suspended or cascading work was scheduled, // In which case– leave pending interactions until the subsequent render. const pendingInteractionMap = root.pendingInteractionMap; pendingInteractionMap.forEach( (scheduledInteractions, scheduledExpirationTime) => { // Only decrement the pending interaction count if we're done. // If there's still work at the current priority, // That indicates that we are waiting for suspense data. if (scheduledExpirationTime > earliestRemainingTimeAfterCommit) { pendingInteractionMap.delete(scheduledExpirationTime); scheduledInteractions.forEach(interaction => { interaction.__count--; if (subscriber !== null && interaction.__count === 0) { try { subscriber.onInteractionScheduledWorkCompleted(interaction); } catch (error) { // It's not safe for commitRoot() to throw. // Store the error for now and we'll re-throw in finishRendering(). if (!hasUnhandledError) { hasUnhandledError = true; unhandledError = error; } } } }); } }, ); } } }
onCommitfunction onCommit(root, expirationTime) {
root.expirationTime = expirationTime;
root.finishedWork = null; // 因为 finishedWork 已经被 commit 掉了
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。