React 中为什么不直接使用 requestIdleCallback?

首先看下 requestIdleCallback是什么?

  1. 简介
    requestIdleCallback 是一个在浏览器空闲时执行低优先级任务的 API。

  2. 定义与用途

    requestIdleCallback 方法允许开发者在浏览器的空闲时段内调度函数的执行。这些函数通常用于执行非关键性的、低优先级的任务,如统计数据发送、非核心动画或页面渲染优化等。它旨在确保高优先级任务(如用户交互、关键动画等)能够优先得到处理,从而避免页面卡顿或性能下降

  3. 工作原理

    当浏览器的主线程处于空闲状态时,requestIdleCallback调用指定的回调函数。回调函数会接收一个名为 deadline 的对象作为参数,该对象包含两个属性:

    • timeRemaining():返回一个表示浏览器空闲时间剩余量的毫秒数。开发者可以利用这个值来判断是否还有足够的空闲时间来执行任务。
    • didTimeout:一个布尔值,用于指示回调函数是否因为超出了指定的超时时间而被强制执行。如果为 true,则表示回调函数是由于超时而被调用的。
  4. 使用示例

    javascript 复制代码
    // 定义一个任务列表
    const tasks = [];
    
    // 添加任务到任务列表
    function addTask(task) {
      tasks.push(task);
      // 如果当前没有正在进行的请求,则请求下一个空闲时段来执行任务
      if (tasks.length === 1) {
        requestIdleCallback(processIdleTasks);
      }
    }
    
    // 在空闲时段内执行任务
    function processIdleTasks(deadline) {
      while ((deadline.timeRemaining() > 0 || deadline.didTimeout) && tasks.length > 0) {
        const task = tasks.shift();
        task();
      }
      // 如果还有未完成的任务,则继续请求下一个空闲时段
      if (tasks.length > 0) {
        requestIdleCallback(processIdleTasks);
      }
    }
    
    // 添加低优先级任务
    addTask(() => console.log('Task 1'));
    addTask(() => console.log('Task 2'));
    addTask(() => console.log('Task 3'));

    在这个示例中,我们定义了一个任务列表 tasks 和两个函数 addTaskprocessIdleTasksaddTask 函数用于将任务添加到任务列表中,并在必要时请求下一个空闲时段来执行任务processIdleTasks 函数则在浏览器的空闲时段内循环执行任务,直到没有剩余空闲时间或没有未完成的任务为止。

React 中不直接使用 requestIdleCallback 的原因涉及多个方面

  1. 控制精细度不足
    React 需要对渲染任务进行精细的控制,以确保用户界面的响应性和性能requestIdleCallback 虽然提供了一种在浏览器空闲时执行低优先级任务的方法,但其控制精细度相对较低。React 需要根据组件的优先级更新的紧急程度等信息来安排渲染任务,而 requestIdleCallback 只能保证在浏览器空闲时执行任务,无法提供足够的控制来满足 React 的需求
  2. 跨浏览器兼容性问题
    requestIdleCallback 在不同浏览器中的支持程度存在差异。虽然现代浏览器普遍支持该 API,但在一些旧版本或特定浏览器(如 Safari 的某些版本)中可能不受支持或存在兼容性问题。React 需要一个能够跨各个版本或框架的解决方案,以确保一致的性能体验。因此,直接使用 requestIdleCallback 可能会导致在不同浏览器中出现不一致的行为
  3. 时间片化机制的需求
    React 使用了时间片化(Time Slicing)技术来优化渲染性能。时间片化允许 React 将渲染任务分割成多个小任务,并在多个帧中交错执行,从而避免长时间占用主线程并导致用户界面卡顿。React 的调度器可以根据任务的优先级和紧急程度来动态调整时间片的分配。而 requestIdleCallback 只能保证在浏览器空闲时执行任务,无法提供时间片化所需的灵活性和动态调整能力
  4. 更丰富的调度策略
    React 的调度器提供了比 requestIdleCallback 更丰富的调度策略和特性。例如,React 的调度器支持多种优先级级别(如立即执行、用户阻塞、正常、低优先级和空闲执行任务等),可以根据任务的优先级来动态调整渲染顺序。此外,React 的调度器还支持任务的中断和恢复,以便在需要时暂停当前任务并处理更高优先级的任务。这些特性使得 React 能够更好地管理任务的优先级和渲染顺序,从而提供更高质量的用户体验。

番外篇

  1. react的调度原理
    React的调度原理是其性能优化的核心机制之一,它主要涉及任务调度、优先级管理以及时间分片等技术。以下是对React调度原理的详细解释:

    • 任务调度

      React通过调度器(Scheduler)来控制任务的执行顺序和时机。调度器是React运行时中的中枢,它负责接收任务并将其放入适当的队列中,然后根据任务的优先级和当前浏览器的空闲状态来决定何时执行任务

      • 任务队列 :React使用多个队列来存储任务,这些队列根据任务的优先级和类型进行分类。常见的队列包括同步队列(syncQueue)、异步队列(asyncQueue)以及定时器队列(TimerQueue)等。
      • 任务入队 :当一个新的任务被创建时,它会被封装成一个Fiber对象(React 16中引入的调度器的核心工作单元),并根据其优先级和类型被放入相应的队列中
      • 任务执行调度器会遍历任务队列,并根据任务的优先级和当前浏览器的空闲状态来决定何时开始执行任务。在执行任务时,调度器会采用时间分片的技术来避免长时间占用主线程
    • 优先级管理

      React中的任务根据其重要性和紧急性被赋予不同的优先级。优先级管理使得React能够更有效地利用浏览器的资源,确保高优先级任务能够优先得到处理

      • 优先级类型 :React内部定义了多种优先级类型,包括事件优先级(Event Priority)、车道优先级(Lane Priority)和调度优先级(Scheduler Priority)等。这些优先级类型通过不同的数值和位运算来表示。
      • 优先级转换 :在任务创建和调度过程中,React会进行优先级转换,将不同优先级类型映射到统一的调度优先级上。这使得调度器能够根据统一的优先级标准来管理和执行任务。
    • 时间分片
      时间分片是React性能优化的关键技术之一。它通过将长任务拆分成多个小任务并在浏览器的空闲时段内逐个执行,从而避免了长时间占用主线程导致的页面卡顿问题

      • 时间切片周期 :React的时间切片周期通常设置为5毫秒(ms),这意味着每个小任务在执行一段时间后(不超过5ms)会主动让出主线程的控制权,以便浏览器能够处理其他紧急任务(如用户输入、UI绘制等)。
      • 中断与恢复 :在时间分片机制下,如果一个任务在执行过程中被中断(例如,因为浏览器的其他高优先级任务需要执行),React会记住该任务的执行状态并在下一个空闲时段内继续执行它。这种中断和恢复的能力使得React能够更有效地利用浏览器的资源并提升用户体验。
  2. 总结调度流程

    React的调度流程涉及多个步骤和组件的协同工作,包括任务的创建、入队、执行以及中断与恢复等。以下是React调度流程的一个简要概述:

    • 任务创建 :当用户触发更新(例如,通过调用setState或forceUpdate等方法)时,React会创建一个新的任务并将其封装成一个Fiber对象
    • 任务入队 :根据任务的优先级和类型,React将任务放入相应的队列中(如同步队列、异步队列等)。
    • 任务执行 :调度器遍历任务队列并根据优先级和当前浏览器的空闲状态来决定何时开始执行任务。在执行任务时,React会采用时间分片的技术来避免长时间占用主线程。
    • 中断与恢复 :如果一个任务在执行过程中被中断(例如,因为浏览器的其他高优先级任务需要执行),React会记住该任务的执行状态并在下一个空闲时段内继续执行它
相关推荐
m0_748248771 小时前
【前端 Uniapp】使用Vant打造Uniapp项目(避坑版)
前端·uni-app
深海的鲸同学 luvi2 小时前
高德地图离线加载解决方案(内网部署)+本地地图瓦片加载
前端·javascript·html5
码字哥3 小时前
EasyExcel设置表头上面的那种大标题(前端传递来的大标题)
java·服务器·前端
GIS好难学4 小时前
《Vue进阶教程》第六课:computed()函数详解(上)
前端·javascript·vue.js
nyf_unknown4 小时前
(css)element中el-select下拉框整体样式修改
前端·css
m0_548514774 小时前
前端打印功能(vue +springboot)
前端·vue.js·spring boot
执键行天涯5 小时前
element-plus中的resetFields()方法
前端·javascript·vue.js
Days20505 小时前
uniapp小程序增加加载功能
开发语言·前端·javascript
喵喵酱仔__5 小时前
vue 给div增加title属性
前端·javascript·vue.js
dazhong20125 小时前
HTML前端开发-- Iconfont 矢量图库使用简介
前端·html·svg·矢量图·iconfont