关于性能问题,我们前文介绍过了八股文篇、实战版本以及canvas性能相关的实操。今天,我们结合前文介绍过的google的performance工具,来更加深入的聊一聊。

在web性能领域,有一个单独的单词long task,就是所谓的长任务。一般来讲,耗时超过50ms即可被认为长任务。这种长任务会导致什么结果呢?大佬解答:

If the user is attempting to interact with the page while a long task runs—or if an important rendering update needs to happen—the browser will be delayed in handling that work.

简言之:卡死你

以目下的一个业务场景为例。该项目的某个页面加载耗时,长达6s。打开performance工具分析发现,符合长任务定义的操作,存在还不止一个,且单个耗时远超50ms,如下图所示:

图片描述

针对上述的“long task”,我们怎么优化呢?

  1. 异步化任务图片描述

依据向主线程妥协的原则,有些场景下的一些任务,不需要等待他完成。例如页面中有个列表模块,初次加载页面用户是看不到的,只有当我们点击了按钮后才会显示。那么对于这个组件的js代码,我们就可以将它从主线程剥离出来。

图片描述

图中的iniData函数就是该组件触发的。单单这一个函数额耗时,就超过了50ms。为此,我们可以用定时器剥离之。(注意,不一定非得用定时器,promise等都可以)我们现在再来看一看总的long task的情况:

图片描述

结果就是:原本一个巨长的long task,被一分为二。

关于为什么要将任务尽可能地切分 官方解释:

This matters because when tasks are broken up, the browser has more opportunities to respond to higher-priority work—and that includes user interactions.

白话就是,通过这种分解长任务,使得浏览器有更多的机会去处理其他高优先级的事情,比如响应用户的互动操作。如下图所示:
图片描述

所以,将一些用户不可见的操作异步化,能够很好的切分long task。

但是吧,某些情况下,没法通过定时器这种方式,比如有一个很大的数组,我们需要遍历其中的每一项做一些操作。可能每一项处理的速度很快,但是因为数量过大导致总的时间长。这种情况下咋搞?

function processData () {
  for (const item of largeDataArray) {
    // Process the individual item here.
  }
}

方案就是使用其他的api,比如requestIdleCallback、postMessage等。其中的RequestIdleCallback就是我们之前说的raf,帧内执行回调,但需要注意的是,执行的条件是帧内有空闲时间(idle),所以优先级略低。

  1. worker
    如果纯粹是计算量过大,那我们完全可以将计算的工作扔给worker去处理即可,毕竟从主线程的角度来看,我们需要的仅仅是所需的数据而已。

  2. 使用async/await

    Yielding to the main thread creates opportunities for critical work to run sooner.

  3. 优先级调度机制

scheduler.postTask(() => {
  console.log('1')
}, { priority: 'background' })
scheduler.postTask(() => {
  console.log('2')
}, { priority: 'user-blocking' })
scheduler.postTask(() => {
  console.log('3')
}, { priority: 'background' })

postTask去指定任务的优先级别。包含三个选项:user-blocking、user-visible、background。但是该api尚未成为标准,须谨慎使用。

总结

  1. Yield to the main thread for critical, user-facing tasks.
  2. Use isInputPending() to yield to the main thread when the user is trying to interact with the page. 3. Prioritize tasks with postTask().
  3. Finally, do as little work as possible in your functions.

严重参考文档: https://web.dev/optimize-long-tasks/?utm_source=devtools