桂林网站建设服务,个人网站广告联盟搭建,外链百科,途牛企业网站建设方案resetChildExpirationTime 1 #xff09;概述
在 completeUnitOfWork 当中#xff0c;有一步比较重要的一个操作#xff0c;就是重置 childExpirationTimechildExpirationTime 是非常重要的一个时间节点#xff0c;它用来记录某一个节点的子树当中#xff0c;目前优先级最…resetChildExpirationTime 1 概述
在 completeUnitOfWork 当中有一步比较重要的一个操作就是重置 childExpirationTimechildExpirationTime 是非常重要的一个时间节点它用来记录某一个节点的子树当中目前优先级最高的那个更新整个应用的调度过程当中使用的都是root节点在 scheduleWork 的时候即便我们创建更新的那个节点是我们写的某一个组件但最终要先找到那一个root节点然后再把它放到调度队列当中因为会有这样的一个情况的存在所以我们对于一个 reactApp 来说它某一个节点下面可能是会存在非常多的一个子树的每棵子树它创建的不同的任务它的 expirationTime 都会不一样的通过 childExpirationTime 来集中最终可以在 root 上面能够快速的找到整个应用当中优先级最高的那个任务举个例子假设同时在 Input 和 List 两个组件内都去创建了一个异步的更新创建这个异步的更新的过程当中 假设Input的 ExpirationTime 优先级比较高List 的 expirationTime 优先级比较少对于div节点来说它记录的是Input节点的 expirationTime, 因为它的优先级比较高对于div来说它如果下一个任务要去更新它的优先级肯定是先更新 Input而不会先更新 List对于 RootFiber 和 App来说因为它们的child只有div, 所以它们记录的会是 div 上面指定的那个优先级最高的 expirationTime所以说 childExpirationTime 对于有分叉的点来说是非常重要的, 它可以记录不同的子树所创建的不同的更新如果我们的分叉变得越来越多有非常多个的时候那么用这种方式来记录它的效率明显是会是更高一点的对于这个情况下如果我 Input 这个更新已经执行完了它上面已经没有 expirationTime因为它没有任务要去更新了对于div来说这个时候如果它还是认为这个Input它之前的 expirationTime 是最高优先级的话那就不对了之后的更新可能就会有出现问题所以对于div这个节点我们执行了 completeUnitOfWork 之后要去更新它的 childExpirationTime对于每一个节点都是一样的, 在上述这个例子里面特别重要的一点就是 div, div 要更新那么它上面的节点都是要更新的因为它们之前记录的都是 div 记录的那个值所以它们执行 completeUnitOfWork 的时候也都要去做相同的操作
2 源码
定位到 packages/react-reconciler/src/ReactFiberScheduler.js#L989
对应到代码里面来看在 completeUnitOfWork 里面我们这边执行完了 completeWork 马上会执行 resetChildExpirationTime 那么这个方法它具体做了什么呢
function resetChildExpirationTime(workInProgress: Fiber,renderTime: ExpirationTime,
) {// 首先判断 renderTime 就是当前正在执行更新的那一个优先级对应的 expirationTime// 如果它不等于never并且 workInProgress.childExpirationTime 等于 Never// 也就是说 workInProgress 就是我们当前节点的子节点优先级最高的那个任务是never// 就是永远不会更新到的并且我们现在也不是正在执行never就是永远不会更新到的那些节点的更新// 说明我们这边根本就不需要做任何的操作所以我们直接return就可以了if (renderTime ! Never workInProgress.childExpirationTime Never) {// The children of this component are hidden. Dont bubble their// expiration times.return;}let newChildExpirationTime NoWork;// Bubble up the earliest expiration time.// 跳过这个if, 直接到 elseif (enableProfilerTimer workInProgress.mode ProfileMode) {// Were in profiling mode.// Lets use this same traversal to update the render durations.let actualDuration workInProgress.actualDuration;let treeBaseDuration workInProgress.selfBaseDuration;// When a fiber is cloned, its actualDuration is reset to 0.// This value will only be updated if work is done on the fiber (i.e. it doesnt bailout).// When work is done, it should bubble to the parents actualDuration.// If the fiber has not been cloned though, (meaning no work was done),// Then this value will reflect the amount of time spent working on a previous render.// In that case it should not bubble.// We determine whether it was cloned by comparing the child pointer.const shouldBubbleActualDurations workInProgress.alternate null ||workInProgress.child ! workInProgress.alternate.child;let child workInProgress.child;while (child ! null) {const childUpdateExpirationTime child.expirationTime;const childChildExpirationTime child.childExpirationTime;if (childUpdateExpirationTime newChildExpirationTime) {newChildExpirationTime childUpdateExpirationTime;}if (childChildExpirationTime newChildExpirationTime) {newChildExpirationTime childChildExpirationTime;}if (shouldBubbleActualDurations) {actualDuration child.actualDuration;}treeBaseDuration child.treeBaseDuration;child child.sibling;}workInProgress.actualDuration actualDuration;workInProgress.treeBaseDuration treeBaseDuration;} else {// 这边主要是有一个 while循环, 这个while循环它首先使用的是 workInProgress.child// 它就是我们当前节点的child然后获取child的 expirationTime 以及 childChildExpirationTime// child.expirationTime 是它自身它所创建的更新对应的 expirationTime// 而 child.childExpirationTime 是child的子树里面任何几个节点它所创建的更新所对应的优先级最高的 expirationTimelet child workInProgress.child;while (child ! null) {// 为什么这边要用这两个值因为我们没有办法直接拿到当前节点它所有子树最高优先级的点// 只能是通过去遍历它的所有的第一层的子节点以及每个子节点它的 childExpirationTime// 因为我们这个 completeUnitOfWork 是由底往上去更新的一个过程// 那么由底往上更新的过程就会每一个节点都会对应这个操作// 对应的这个操作之后它肯定就是每一个节点都会更新到优先级最高的那个 expirationTimeconst childUpdateExpirationTime child.expirationTime;const childChildExpirationTime child.childExpirationTime;// 接下来进行判断更新if (childUpdateExpirationTime newChildExpirationTime) {newChildExpirationTime childUpdateExpirationTime;}if (childChildExpirationTime newChildExpirationTime) {newChildExpirationTime childChildExpirationTime;}// 下一个节点child child.sibling;}}workInProgress.childExpirationTime newChildExpirationTime;
}以上注释写在代码里简单来说 就是在每一次 complete 一个节点之后就要去重设它的 childExpirationTime 的一个过程