优化 First Input Delay

如何更快地响应用户互动。

点击链接后,什么都不会发生!为什么我无法与此网页互动?😢

First Contentful Paint (FCP) 和 Largest Contentful 绘制 (LCP) 都是用于衡量内容呈现所需时间的指标 在网页上的视觉呈现(绘制)效果。虽然很重要,但绘制时间不会捕获加载 响应速度:网页对用户互动的响应速度。

First Input Delay (FID) 是一项核心网页指标指标,用于捕获用户的 对网站的交互性和响应性的第一印象。它衡量的是 当浏览器真正能够响应该网页时, 互动FID 是字段指标,不能是 在实验室环境中进行模拟。必须有真实的用户互动,才能衡量 响应延迟。

较好的保真值是 2.5 秒,差值大于 4.0 秒,介于 0.5 秒之间的所有值都需要改进

为了帮助预测实验中的 FID,我们 建议使用 Total Blocking Time (TBT)。它们衡量不同的指标 TBT 的改进通常对应于 FID 的改进。

FID 不佳的主要原因是大量 JavaScript 执行。优化 JavaScript 解析方式 会直接降低 FID。

大量 JavaScript 执行

当浏览器在主线程上执行 JavaScript 时,无法响应大多数用户输入。换言之, 当主线程处于忙碌状态时,浏览器无法响应用户互动。改进措施:

拆分冗长的任务

如果您已经尝试减少要在单个网页上加载的 JavaScript 数量, 将长时间运行的代码分解为较小的异步任务非常有用。

长任务是指 JavaScript 执行期,用户可能在 您的界面无响应任何阻塞主线程 50 毫秒或更长时间的代码段可能都是 就是一个长任务。冗长的任务意味着 潜在的 JavaScript 膨胀(加载和执行超出用户当前可能需要的内容)。 拆分冗长的任务可以缩短网站上的输入延迟。

Chrome 开发者工具中的长时间任务
Chrome 开发者工具在性能面板中直观呈现长时间运行的任务

随着您采用代码拆分和拆分 耗时较长的任务。虽然 TBT 不是实测指标,但对于查看最终进展情况非常有用 改进可交互时间 (TTI) 和 FID。

针对互动准备情况优化网页

在严重依赖的 Web 应用中,有很多常见原因会导致 FID 和 TBT 得分较低 JavaScript:

第一方脚本执行可能会延迟互动准备情况

  • JavaScript 数据膨胀、执行时间繁琐和分块效率低下,可能会拖慢 页面可以响应用户输入并影响 FID、TBT 和 TTI。逐步加载代码和 功能有助于推广这项工作,并提高互动就绪性。
  • 服务器端渲染的应用看起来像是在屏幕上绘制了像素 但要注意用户交互被大型脚本执行(例如 re-hydration 以连接事件监听器)。这可能需要几百毫秒的时间 如果使用基于路由的代码拆分,甚至是几秒钟。考虑转移更多逻辑 或在构建时静态生成更多内容。

以下是针对 应用。通过将非重要组件代价高昂的脚本加载(和执行)转移到 关键路径,用户能够更早地与页面进行交互。

优化第一方脚本后,Lighthouse 中的 TBT 得分有所提高。

数据获取可能会影响交互准备的许多方面

  • 等待级联提取(例如组件的 JavaScript 和数据提取)的瀑布流 影响互动延迟时间旨在尽量减少对级联数据提取的依赖。
  • 大型内联数据存储区可能会挤出 HTML 解析时间,并影响绘制和交互 指标。旨在尽量减少需要在客户端进行后处理的数据量。

第三方脚本执行也会延迟互动延迟时间

  • 许多网站包含第三方跟踪代码和分析功能,这会使网络处于忙碌状态, 使主线程定期无响应,从而影响交互延迟时间。探索 按需加载第三方代码(例如,可能不要加载非首屏广告,除非 它们会滚动到更靠近视口的位置)。
  • 在某些情况下,第三方脚本可以根据优先级和 同时延迟了页面多久可以进行交互。尝试 优先加载您认为能为用户提供最大价值的内容。

使用 Web Worker

阻塞的主线程是导致输入延迟的主要原因之一。网站 通过工作器可以运行 JavaScript 在后台线程上执行将非界面操作移至单独的工作器线程可能会切断主线程 从而改善 FID。

不妨考虑使用以下库,以便更轻松地在您的网站上使用 Web Worker:

  • Comlink:一个帮助程序库,用于 postMessage,并使其更易于使用
  • 工作方式:通用 Web 工作器导出器
  • Workerize:将模块移动到 Web worker

缩短 JavaScript 执行时间

限制网页上的 JavaScript 数量可缩短浏览器需要执行 。这样可加快浏览器对任何请求的响应速度, 用户互动

要减少在网页上执行的 JavaScript 数量,请执行以下操作:

  • 推迟加载未使用的 JavaScript
  • 尽量减少未使用的 polyfill

推迟加载未使用的 JavaScript

默认情况下,所有 JavaScript 都会阻塞渲染。当浏览器遇到链接到 它必须暂停正在执行的操作,然后下载、解析、编译和执行 该 JavaScript。因此,您应该只加载网页或 响应用户输入。

Chrome 中的覆盖率标签页 开发者工具可以告诉您网页上有多少 JavaScript 未被使用。

“覆盖率”标签页。

要减少未使用的 JavaScript,请执行以下操作:

  • 将 bundle 的代码拆分为多个数据块
  • 使用 asyncdefer 延迟所有非关键 JavaScript,包括第三方脚本

代码拆分是指将单个大型 JavaScript 软件包拆分为多个较小区块的概念 可有条件地加载(也称为延迟加载)。 大多数较新的浏览器都支持动态导入语法, 支持按需提取模块:

import('module.js').then((module) => {
  // Do something with the module.
});

在某些用户互动(如更改路线或 显示模态窗口)将确保仅在 所需的资源。

除了常规的浏览器支持之外,动态导入语法还可用于许多不同的 build 系统。

  • 如果您使用的是 webpack汇总、 或 Parcel 作为模块打包器,充分利用 动态导入支持。
  • 客户端框架,例如 ReactAngularVue 提供 以便更轻松地在组件级别进行延迟加载。

除代码拆分外,请务必使用异步或 defer 来修改对 关键路径或首屏内容

<script defer src="…"></script>
<script async src="…"></script>

除非有特殊原因,否则所有第三方脚本都应使用 defer 加载 或 async

尽量减少未使用的 polyfill

如果您使用现代 JavaScript 语法编写代码并引用现代浏览器 API, 需要对其进行转译并添加 polyfill 才能使其在旧版浏览器中正常运行。

在网站中加入 polyfill 和转译代码的一个主要性能问题是, 如果使用较新版本的浏览器,则不应该要求进行下载。缩短 JavaScript 缩减应用大小,尽可能减少未使用的 polyfill 并将其使用范围限制为 所需的环境

如需优化网站上的 polyfill 用法,请执行以下操作:

  • 如果您使用 Babel 作为转译器,请使用 @babel/preset-env,以便仅包含 polyfill 您计划定位的浏览器所需达到的最低标准对于 Babel 7.9,请启用 用于进一步缩减大小的 bugfixes 选项 任何不需要的 polyfill
  • 使用 module/nomodule 模式提供两个单独的 bundle(另外,@babel/preset-env 通过 target.esmodules 支持此功能)

    <script type="module" src="modern.js"></script>
    <script nomodule src="legacy.js" defer></script>
    

    许多使用 Babel 编译的较新 ECMAScript 功能已在环境中受支持 支持 JavaScript 模块的 API。这样,你就可以简化相应流程 只有实际需要转译的代码才会用到。

开发者工具

有许多工具可用于测量和调试 FID:

在此感谢 Philip Walton、Kayce Basques、Ilya Grigorik 和 Annie Sullivan 对我们的评价。