React执行调度流程梳理笔记
React执行调度流程梳理笔记
触发更新
我们最常见的触发更新的方式就是更新state了,可以分别从类组件和函数组件看看这时会发生什么。
类组件之 setState:
当触发setState
本质上是调用enqueueSetState
。
enqueueSetState(inst,payload,callback){
const update = createUpdate(eventTime, lane);
enqueueUpdate(fiber, update, lane);
const root = scheduleUpdateOnFiber(fiber, lane, eventTime);
}
函数组件之 useState
再看一下 hooks 的useState
。
function dispatchAction(fiber, queue, action) {
var lane = requestUpdateLane(fiber);
scheduleUpdateOnFiber(fiber, lane, eventTime);
}
可以看出来useState
,setState
最后都是调用scheduleUpdateOnFiber
方法。
看看scheduleUpdateOnFiber
方法的原理吧
export function scheduleUpdateOnFiber(fiber,lane,eventTime){
if (lane === SyncLane) {
if (
(executionContext & LegacyUnbatchedContext) !== NoContext && // unbatch 情况,比如初始化
(executionContext & (RenderContext | CommitContext)) === NoContext) {
/* 开始同步更新,进入到 workloop 流程 */
`performSyncWorkOnRoot`
2. 进入调度(root);
}else{
/* 进入调度,把任务放入调度中 */
ensureRootIsScheduled(root, eventTime);
if (executionContext === NoContext) {
/* 当前的执行任务类型为 NoContext ,说明当前任务是非可控的,那么会调用 flushSyncCallbackQueue 方法。 */
flushSyncCallbackQueue();
}
}
}
}
可以看主要分两类
- 直接触发
performSyncWorkOnRoot
,接下来就可以进入调和阶段performSyncWorkOnRoot
了。 - 进入调度阶段
ensureRootIsScheduled
,但是里面部分场景会直接触发flushSyncCallbackQueue
2.1 非可控任务:比如是在延时器(timer)队列或者是微任务队列(microtask) 2.2 空闲期的同步任务:避免出现在浏览器空闲状态下发生一次 state 更新,但还需要等到下一次空闲帧才执行
进入调度阶段ensureRootIsScheduled
是做什么呢?
function ensureRootIsScheduled(root,currentTime){
/* 计算一下执行更新的优先级 */
var newCallbackPriority = returnNextLanesPriority();
/* 当前 root 上存在的更新优先级 */
const existingCallbackPriority = root.callbackPriority;
/* 如果两者相等,那么说明是在一次更新中,那么将退出 */
if(existingCallbackPriority === newCallbackPriority){
return
}
if (newCallbackPriority === SyncLanePriority) {
/* 在正常情况下,会直接进入到调度任务中。 */
newCallbackNode = scheduleSyncCallback(performSyncWorkOnRoot.bind(null, root));
}else{
/* 这里先忽略 */
}
/* 给当前 root 的更新优先级,绑定到最新的优先级 */
root.callbackPriority = newCallbackPriority;
}
ensureRootIsScheduled
主要做的事情有:
- 首先会计算最新的调度更新优先级 newCallbackPriority,接下来获取当前 root 上的 callbackPriority 判断两者是否相等。如果两者相等,那么将直接退出不会进入到调度中。
- 如果不想等那么会真正的进入调度任务 scheduleSyncCallback 中。注意的是放入调度中的函数就是调和流程的入口函数
performSyncWorkOnRoot
。 - 函数最后会将 newCallbackPriority 赋值给 callbackPriority。
那么如果在正常模式下(非异步)一次更新中触发了多次setState
或者useState
,那么第一个setState
进入到ensureRootIsScheduled
就会有 root.callbackPriority = newCallbackPriority
,那么接下来如果还有setState
或(useState
),那么就会退出,将不进入调度任务中,原来这才是批量更新的原理,多次触发更新只有第一次会进入到调度中。
总结
不管是直接执行还是进入调度,都是对调和流程的入口函数performSyncWorkOnRoot
的处理,区别只不过是何时执行的时机而已,因为最终都会以此进入调和流程。
- 分类:
- Web前端
相关文章
使用next.js服务端渲染经历
上周末的时候打算把自己的网站从vue的ssr转换为react的ssr,鉴于之前在vue中是用的原生的ssr,这次想在react中试试框架,所以首选的就是next.js。 第一次用next.js,根据 阅读更多…
邮箱收件人组件成长历程(二)(React hooks升级版)
记得自己之前写过一篇 《邮箱收件人组件(vue版)成长历程(一)》 记得当时里面写到了自己使用的是可编辑div来进行输入的,同时提到 当时出于挑战自己和青铜的倔强,想试着换个方案,完全使用可编辑di 阅读更多…
React router用hooks读取routeName、根据routeName跳转
在迁移Vue至React的过程中遇到了一些路由相关的问题,在Vue项目中经常会使用routeName,毕竟使用path太长了,也记不住,我自己看了看React router也没有发现routeNam 阅读更多…
2021年的一点工作总结(一)迁移React技术栈
2021年全年的工作总结起来很简单,算是做苦力的一年吧。。。 2021年春节后就开始邮件项目从Vue迁移到React的工作以及富文本编辑器由wangEditor替换成CKEditor。 其实自己 阅读更多…
改造富文本编辑器wangEditor成react组件
我们知道wangEditor常用的功能是editor实例的 txt.html() 和 txt.text() 方法,尤其是 txt.html() 方法,这是一个类似与jQuery常用的那种get和se 阅读更多…
对React Hooks的Capture value特性的理解
之前我的项目里面很多功能都是用的事件驱动,所以下面的实例也会更多地使用监听事件的回调函数。 我们先看下测试代码 const {useEffect ,useState, useRef, us 阅读更多…