经过前几章的学习,我们终于有足够的前置知识理解状态更新的整个流程。
这一章我们看看几种常见的触发状态更新的方法是如何完成工作的。
几个关键节点
在开始学习前,我们先了解源码中几个关键节点(即几个关键函数的调用)。通过这章的学习,我们会将这些关键节点的调用路径串起来。
先从我们所熟知的概念开始。
render阶段的开始
我们在render阶段流程概览一节讲到,
render阶段
开始于performSyncWorkOnRoot
或performConcurrentWorkOnRoot
方法的调用。这取决于本次更新是同步更新还是异步更新。
commit阶段的开始
我们在commit阶段流程概览一节讲到,
commit阶段
开始于commitRoot
方法的调用。其中rootFiber
会作为传参。
我们已经知道,render阶段
完成后会进入commit阶段
。让我们继续补全从触发状态更新
到render阶段
的路径。
触发状态更新(根据场景调用不同方法)
|
|
v
?
|
|
v
render阶段(`performSyncWorkOnRoot` 或 `performConcurrentWorkOnRoot`)
|
|
v
commit阶段(`commitRoot`)
创建Update对象
在React
中,有如下方法可以触发状态更新(排除SSR
相关):
ReactDOM.render
this.setState
this.forceUpdate
useState
useReducer
这些方法调用的场景各不相同,他们是如何接入同一套状态更新机制呢?
答案是:每次状态更新
都会创建一个保存更新状态相关内容的对象,我们叫他Update
。在render阶段
的beginWork
中会根据Update
计算新的state
。
我们会在下一节详细讲解Update
。
从fiber到root
现在触发状态更新的fiber
上已经包含Update
对象。
我们知道,render阶段
是从rootFiber
开始向下遍历。那么如何从触发状态更新的fiber
得到rootFiber
呢?
答案是:调用markUpdateLaneFromFiberToRoot
方法。
你可以从这里 (opens new window)看到
markUpdateLaneFromFiberToRoot
的源码
该方法做的工作可以概括为:从触发状态更新的fiber
一直向上遍历到rootFiber
,并返回rootFiber
。
由于不同更新优先级不尽相同,所以过程中还会更新遍历到的fiber
的优先级。这对于我们当前属于超纲内容。
调度更新
现在我们拥有一个rootFiber
,该rootFiber
对应的Fiber树
中某个Fiber节点
包含一个Update
。
接下来通知Scheduler
根据更新的优先级,决定以同步还是异步的方式调度本次更新。
这里调用的方法是ensureRootIsScheduled
。
以下是ensureRootIsScheduled
最核心的一段代码:
if (newCallbackPriority === SyncLanePriority) {
// 任务已经过期,需要同步执行render阶段
newCallbackNode = scheduleSyncCallback(
performSyncWorkOnRoot.bind(null, root)
);
} else {
// 根据任务优先级异步执行render阶段
var schedulerPriorityLevel = lanePriorityToSchedulerPriority(
newCallbackPriority
);
newCallbackNode = scheduleCallback(
schedulerPriorityLevel,
performConcurrentWorkOnRoot.bind(null, root)
);
}
你可以从这里 (opens new window)看到
ensureRootIsScheduled
的源码
其中,scheduleCallback
和scheduleSyncCallback
会调用Scheduler
提供的调度方法根据优先级
调度回调函数执行。
可以看到,这里调度的回调函数为:
performSyncWorkOnRoot.bind(null, root);
performConcurrentWorkOnRoot.bind(null, root);
即render阶段
的入口函数。
至此,状态更新
就和我们所熟知的render阶段
连接上了。
总结
让我们梳理下状态更新
的整个调用路径的关键节点:
触发状态更新(根据场景调用不同方法)
|
|
v
创建Update对象(接下来三节详解)
|
|
v
从fiber到root(`markUpdateLaneFromFiberToRoot`)
|
|
v
调度更新(`ensureRootIsScheduled`)
|
|
v
render阶段(`performSyncWorkOnRoot` 或 `performConcurrentWorkOnRoot`)
|
|
v
commit阶段(`commitRoot`)
总结
本节我们了解了状态更新的整个流程。
在接下来三节中,我们会花大量篇幅讲解Update
的工作机制,因为他是构成React concurrent mode
的核心机制之一。