深入理解React调度(Scheduler)原理

 更新时间:2022年07月04日 09:01:20   作者:YuLong~W  
本文主要介绍了深入理解React调度(Scheduler)原理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

异步调度

问题:由于对于大型的 React 应用,会存在一次更新,递归遍历大量的虚拟 DOM ,造成占用 js 线程,使得浏览器没有时间去做一些动画效果,伴随项目越来越大,项目会越来越卡。

对比Vue:

Vue 有这 template 模版收集依赖的过程,轻松构建响应式,使得在一次更新中,Vue 能够迅速响应,找到需要更新的范围,然后以组件粒度更新组件,渲染视图。

React 中,一次更新 React 无法知道此次更新的波及范围,所以 React 选择从根节点开始 diff ,查找不同,更新这些不同。

解决:

把 React 的更新,交给浏览器自己控制,浏览器先执行绘制任务,空闲时间执行更新任务,解决了卡顿问题。即采用异步调度的方法。

时间分片

React让浏览器控制React更新:浏览器每执行一次事件循环都会:处理事件,执行 js,调用requestAnimation,布局 Layout,绘制 Paint,在一次执行后,浏览器进入空闲时,可以执行更新任务

谷歌浏览器提供的一个 API, 在浏览器有空余的时间,浏览器就会调用 requestIdleCallback 的回调。

requestIdleCallback(callback,{ timeout })

  • callback 回调。浏览器空余时间执行回调函数。
  • timeout 超时时间。如果浏览器长时间没有空闲,那么回调就不会执行,为了解决这个问题,可以通过 requestIdleCallback 的第二个参数指定一个超时时间。

React 为了防止 requestIdleCallback 中的任务由于浏览器没有空闲时间而卡死,所以设置了 5 个优先级。

  • Immediate -1 需要立刻执行。
  • UserBlocking 250ms 超时时间250ms,一般指的是用户交互。
  • Normal 5000ms 超时时间5s,不需要直观立即变化的任务,比如网络请求。
  • Low 10000ms 超时时间10s,肯定要执行的任务,但是可以放在最后处理。
  • Idle 一些没有必要的任务,可能不会执行。

模拟requestdleCallback

条件:

  • 可以主动让出主线程,让浏览器去渲染视图。
  • 一次事件循环只执行一次,因为执行一个以后,还会请求下一次的时间片。

宏任务:在下次事件循环中执行,不会阻塞浏览器更新。且浏览器一次只会执行一个宏任务。

1、采用setTimeout(fn, 0),间隔时间会变成 4 毫秒左右,不是最优选方案

2、采用MessageChannel 接口,允许开发者创建一个新的消息通道,并通过它的两个 MessagePort 属性发送数据。

在一次更新中,向浏览器请求执行更新任务,调用 requesetHostCallbcak,将更新任务 函数callback赋值给 scheduleHostCallback,port2 向 port1 发起 postMessage消息通知。

port1 会通过 onmessage,接受来自 port2 消息,执行更新任务 scheduleHostCallback,执行完后,清空任务。

异步调度原理

React 发生一次更新,会统一走 ensureRootIsScheduled(调度应用)

对于 正常更新 会走 performSyncWorkOnRoot 逻辑,最后会走 workLoopSync

对于 低优先级的异步更新 会走 performConcurrentWorkOnRoot 逻辑,最后会走 workLoopConcurrent

区别:异步模式会调用一个 shouldYield(),如果当前浏览器没有空余时间, shouldYield 会中止循环,直到浏览器有空闲时间后再继续遍历,从而达到终止渲染的目的。解决了一次性遍历大量的 fiber ,导致浏览器没有时间执行一些渲染任务,导致了页面卡顿。

1、scheduleCallback

更新任务、异步更新任务都是由调度器 scheduleCallback 统一调度的

正常更新任务:

scheduleCallback(Immediate,workLoopSync)

异步更新任务:

/* 计算超时等级,就是如上那五个等级 */
var priorityLevel = inferPriorityFromExpirationTime(currentTime, expirationTime);
scheduleCallback(priorityLevel,workLoopConcurrent)

scheduleCallback() 函数执行过程

scheduleCallback 流程如下:

  • 创建一个新的任务 newTask。
  • 通过任务的开始时间( startTime ) 和 当前时间( currentTime ) 比较:当 startTime > currentTime, 说明未过期,存到 timerQueue,当 startTime <= currentTime,说明已过期, 存到 taskQueue。
  • 如果任务没有过期,用 requestHostTimeout 延时执行 handleTimeout
  • 如果任务过期,并且没有调度中的任务,那么调度 requestHostCallback
  • 本质上调度的是 flushWork

2、requestHostTimeout

通过 setTimeout 来进行延时指定时间的。

延时执行 handleTimeoutcancelHostTimeout 用于清除当前的延时器。

3、handleTimeout

延时时间后,handleTimeout 会把任务重新放在 requestHostCallback 调度。

通过 advanceTimers 将 timerQueue 中过期的任务转移到 taskQueue 中。然后调用 requestHostCallback 调度过期的任务。

4、advanceTimers

如果任务已经过期,那么将 timerQueue 中的过期任务,放入 taskQueue。

5、flushWork

requestHostCallback ,放入 MessageChannel 中的回调函数是flushWork。

flushWork 如果有延时任务执行的话,那么会先暂停延时任务,然后调用 workLoop ,去真正执行超时的更新任务。

6、workLoop

workLoop 是调度中的 workLoop

React 的更新任务最后都是放在 taskQueue 中

workLoop 会依次更新过期任务队列中的任务

调度流程图

总结

1、异步调度原因

2、时间分片和 requestIdleCallback

3、异步调度原理

4、调度流程

到此这篇关于深入理解React 调度(Scheduler)原理的文章就介绍到这了,更多相关React 调度内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • React+EggJs实现断点续传的示例代码

    React+EggJs实现断点续传的示例代码

    这篇文章主要介绍了React+EggJs实现断点续传的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07
  • React18系列reconciler从0实现过程详解

    React18系列reconciler从0实现过程详解

    这篇文章主要介绍了React18系列reconciler从0实现过程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • React实现核心Diff算法的示例代码

    React实现核心Diff算法的示例代码

    这篇文章主要为大家详细介绍了React如何实现Diff算法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-04-04
  • React如何利用相对于根目录进行引用组件详解

    React如何利用相对于根目录进行引用组件详解

    这篇文章主要给大家介绍了关于React如何使用相对于根目录进行引用组件的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-10-10
  • Hello React的组件化方式之React入门小案例演示

    Hello React的组件化方式之React入门小案例演示

    这篇文章主要介绍了Hello React的组件化方式-React入门小案例,本文通过Hello React的案例, 来体验一下React开发模式, 以及jsx的语法,需要的朋友可以参考下
    2022-10-10
  • React useEffect的理解与使用

    React useEffect的理解与使用

    这篇文章主要介绍了React useEffect的理解与使用,帮助大家更好的理解和学习使用React,感兴趣的朋友可以了解下
    2021-04-04
  • 详解React获取DOM和获取组件实例的方式

    详解React获取DOM和获取组件实例的方式

    这篇文章主要介绍了React获取DOM和获取组件实例的方式,如何创建refs来获取对应的DOM呢?目前有三种方式,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-10-10
  • 如何在React项目中引入字体文件并使用详解

    如何在React项目中引入字体文件并使用详解

    我们项目中通常会需要引入字体,所以下面这篇文章主要给大家介绍了关于如何在React项目中引入字体文件并使用的相关资料,文中通过图文介绍的非常详细,需要的朋友可以参考下
    2022-08-08
  • React diff算法的实现示例

    React diff算法的实现示例

    这篇文章主要介绍了React diff算法的实现示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-04-04
  • React自定义hooks同步获取useState的最新状态值方式

    React自定义hooks同步获取useState的最新状态值方式

    这篇文章主要介绍了React自定义hooks同步获取useState的最新状态值方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03

最新评论