「这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战」
通过前两篇文章我们了解了 scheduleCallback 是怎么实现的,现在回到上一层,看看遗落没有展开的内容。在 ensureRootIsScheduled 中,如果 getHighestPriorityLane 调用的返回值不是 SyncLane的时候,则有以下代码:
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root),
)
root.callbackNode = newCallbackNode
现在我们知道了 scheduleCallback 进行对第二个参数进行调度,performConcurrentWorkOnRoot.bind(null, root) 最终会在某个时机被执行,那么 performConcurrentWorkOnRoot 函数又做了什么关键的操作?
函数内执行的关键函数
忽略与出错时进行恢复操作的代码,关键函数如下:
- flushPassiveEffects:执行某些副作用
- ensureRootIsScheduled:形成递归执行
- renderRootConcurrent:与 renderRootSync 只能两选一,如果执行的是renderRootConcurrent,最后会执行 finishConcurrentRender
- renderRootSync: 与 renderRootConcurrent 二选一
- finishConcurrentRender:执行完成并发渲染的后置操作(我估计是这样的)
renderRootConcurrent
分析一下 renderRootConcurrent 函数。
函数定义如下:
function renderRootConcurrent (root: FiberRoot, lanes: Lanes) {
const prevExecutionContext = executionContext
executionContext |= RenderContext
const prevDispatcher = pushDispatcher()
if (workInProgressRoot !== root || workInProgressRootRenderLanes !== lanes) {
resetRenderTimer()
prepareFreshStack(root, lanes)
}
do {
try {
workLoopConcurrent()
break
} catch (thrownValue) {
handleError(root, thrownValue)
}
} while (true)
resetContextDependencies()
popDispatcher(prevDispatcher)
executionContext = prevExecutionContext
if (workInProgress !== null) {
return RootIncomplete
} else {
workInProgressRoot = null
workInProgressRootRenderLanes = NoLanes
return workInProgressRootExitStatus
}
}
从这个函数的核心实现(已省略调试和 profiler 等不重要的代码)中我们了解到,实现的逻辑主要如下
- 记录原来的 executionContext, 然后给 executionContext 增加渲染上下文 RenderContext
- 保存现场——将 dispatcher 存入栈
- 如果有问题则重置 timer,并 prepareFreshStack
- 执行一看就很关键的 workLoopConcurrent
- 重置上下文依赖,即调用 resetContextDependencies
- 回复现场——从栈中 pop dispatcher
- 恢复原来的 executionContext
- 如果 workInProgress 不为 null,然后返回 RootIncomplete 状态,否则重置 workInProgressRoot 和 workInProgressRootRenderLanes,返回 workInProgressRootExitStatus 状态
workLoopConcurrent
作用就一句话—— 持续调用 performUnitOfWork 直到 workInProgress 为空或者 shouldYield 函数返回 false。
shouldYield 就是 scheduler 库里的 unstable_shouldYield 函数,该函数的作用是当处于需要中断 react的渲染,将控制交还给“主线程”的场景时返回 true,具体来说就是当前时间距离全局变量 startTime 的间隔小于帧最小间隔(约为 5ms)的时候返回 true。
performUnitOfWork
主要是执行 beginWork 逻辑,然后将 workInProgress.pedingProps 的内容复制给 workInProgress.memoizedProps, 最后 ReactCurrentOwner.current 重置为 null。
代码核心实现如下:
function performUnitOfWork (unitOfWork: Fiber): void {
const current = unitOfWork.alternate
setCurrentDebugFiberInDEV(unitOfWork)
let next = beginWork(current, unitOfWork, subtreeRenderLanes)
unitOfWork.memoizedProps = unitOfWork.pendingProps
if (next === null) {
completeUnitOfWork(unitOfWork)
} else {
workInProgress = next
}
ReactCurrentOwner.current = null
}
下期预告
直到现在,我们仍没看到 react 是如何实现 dom 的更新的,dom diff 又是如何进行的,而这部分逻辑隐藏在 beginWork 之下,明天我们再来瞅一瞅 beginWork 的实现。