Preload critical assets to improve loading speed

95 阅读9分钟

当您打开网页时,浏览器会从服务器请求 HTML 文档,解析其内容,并提交对任何引用资源的单独请求。 作为开发人员,您已经了解页面所需的所有资源以及其中最重要的资源。 您可以利用这些知识提前请求关键资源并加快加载过程。 这篇文章解释了如何使用 来实现。

预加载的工作原理

预加载最适合浏览器通常较晚发现的资源。

在此示例中,Pacifico 字体在样式表中使用@font-face 规则定义。 浏览器仅在完成下载和解析样式表后才加载字体文件。

通过预加载某个资源,您是在告诉浏览器您希望比浏览器发现它更快地获取它,因为您确信它对当前页面很重要。

在本例中,Pacifico 字体是预加载的,因此下载与样式表并行进行。

关键请求链表示浏览器优先和获取的资源的顺序。 Lighthouse 将位于该链第三层的资产识别为晚期发现。 您可以使用 Preload key requests 审核来确定要预加载的资源。

您可以通过将带有 rel="preload" 的 标记添加到 HTML 文档的头部来预加载资源:

<link rel="preload" as="script" href="critical.js">

浏览器缓存预加载的资源,以便在需要时立即可用。 (它不执行脚本或应用样式表。)

实施预加载后,包括 Shopify、金融时报和 Treebo 在内的许多网站在以用户为中心的指标(例如交互时间和首次内容绘制)方面都出现了 1 秒的改进。

资源提示(例如 preconnect 和 prefetch)在浏览器认为合适的时候执行。 另一方面,预加载对于浏览器来说是强制性的。 现代浏览器已经非常擅长对资源进行优先级排序,这就是为什么谨慎使用预加载并只预加载最关键的资源很重要。 未使用的预加载会在加载事件后大约 3 秒触发 Chrome 中的控制台警告。

使用场景

所有现代浏览器都支持预加载。

在撰写本文时,Chrome 对预加载的请求比其他优先级更高的资源更快地获取了一个未解决的错误。 在解决此问题之前,请注意预加载资源如何“跳过队列”并比应有的更快被请求。

预加载 CSS 中定义的资源

在浏览器下载并解析这些 CSS 文件之前,不会发现使用 @font-face 规则定义的字体或 CSS 文件中定义的背景图像。 预加载这些资源可确保在 CSS 文件下载之前获取它们。

预加载 CSS 文件

如果您使用的是 critical CSS approach,则将您的 CSS 分成两部分。 呈现首屏内容所需的关键 CSS 内联在文档的 中,非关键 CSS 通常使用 JavaScript 延迟加载。 在加载非关键 CSS 之前等待 JavaScript 执行可能会导致用户滚动时呈现延迟,因此最好使用 更快地启动下载。

预加载JavaScript文件

因为浏览器不执行预加载的文件,所以预加载有助于将获取与执行分开,这可以改善诸如交互时间之类的指标。 如果您拆分 JavaScript 包并仅预加载关键块,则预加载效果最佳。

如何实现rel=preload

实现预加载最简单的方法是在文档的 中添加 标签:

<head>  
    <link rel="preload" as="script" href="critical.js">
</head>

提供 as 属性有助于浏览器根据其类型设置预取资源的优先级,设置正确的标头,并确定资源是否已存在于缓存中。 此属性的可接受值包括:脚本、样式、字体、图像等 others

查看 [Chrome Resource Priorities and Scheduling](https://docs.google.com/document/d/1bCDuq9H1ih9iNjgzyAL0gpwNFiEP4TZS-YLRp_RuMlc/edit) 文档,了解更多关于浏览器如何区分不同类型资源的优先级。

省略 as 属性或具有无效值相当于 XHR 请求,其中浏览器不知道它正在获取什么,因此无法确定正确的优先级。 它还可能导致某些资源(例如脚本)被提取两次。

某些类型的资源(例如字体)以匿名模式加载。 对于那些您必须使用 preload 设置 crossorigin 属性:

<link rel="preload" href="ComicSans.woff2" as="font" type="font/woff2" crossorigin>

没有crossorigin属性的预加载字体将被提取两次!

元素还接受一个 type 属性,该属性包含链接资源的 MIME 类型。 浏览器使用 type 属性的值来确保资源仅在其文件类型受支持时才被预加载。 如果浏览器不支持指定的资源类型,它将忽略 。

 <> 试试看 通过预加载 Web 字体来提高页面的性能

您还可以通过 Link HTTP 标头预加载任何类型的资源:

Link: </css/style.css>; rel="preload"; as="style"

在 HTTP Header 中指定 preload 的一个好处是浏览器不需要解析文档来发现它,这在某些情况下可以提供小的改进。

使用 webpack 预加载 JavaScript 模块

如果您使用的是创建应用程序构建文件的模块捆绑器,您需要检查它是否支持预加载标签的注入。 对于 webpack 4.6.0 或更高版本,通过在 import() 中使用 magic comments 来支持预加载:

import(_/* webpackPreload: true */_ "CriticalChunk")

If you are using an older version of webpack, use a third-party plugin such as preload-webpack-plugin.

预加载对 Core Web Vitals 的影响

预加载是一种强大的性能优化,对加载速度有影响。 此类优化可能会导致您网站的核心 Web Vitals 发生变化,因此了解它们很重要。

Largest Contentful Paint (LCP) 

当涉及到字体和图像时,预加载对最大内容绘制(large Contentful Paint, LCP)有强大的影响,因为图像和文本节点都可以是LCP候选节点。使用网页字体渲染的英雄图像和大量文本可以从适当的预加载提示中获益,当有机会将这些重要的内容更快地传递给用户时,就应该使用这些提示。

 但是,当涉及到预加载和其他优化时,您需要小心!特别是,避免预加载太多的资源。如果有太多的资源被设定优先级,那么实际上它们都没有优先级。过度预载提示的影响对那些带宽争用更加明显的较慢网络尤其有害。 

 相反,应该关注一些高价值的资源,你知道这些资源将从适当的预加载中受益。在预加载字体时,请确保使用WOFF 2.0格式的字体,以尽可能减少资源加载时间。由于WOFF 2.0具有出色的浏览器支持,如果LCP候选节点是文本节点,那么使用WOFF 1.0或TrueType (TTF)等较老的格式将延迟LCP。 

 当涉及到LCP和JavaScript时,您需要确保从服务器发送完整的标记,以便浏览器的预加载扫描器能够正常工作。如果您提供的体验完全依赖于JavaScript来呈现标记,而不能发送服务器呈现的HTML,那么在浏览器预加载扫描器无法进行的地方进行处理,并预加载只有在JavaScript完成加载和执行时才能发现的资源,这将是有利的。

计算布局偏移

Cumulative Layout Shift (CLS) 是涉及 Web 字体的一个特别重要的指标,CLS 与使用 font-display CSS property 来管理字体加载方式的 Web 字体有显着的相互作用。 为了尽量减少与 Web 字体相关的布局变化,请考虑以下策略:

  • 在为 font-display 使用默认 block 值时预加载字体。这是一种微妙的平衡。在没有退路的情况下阻止字体的显示可以被认为是一个用户体验问题。一方面,用font-display: block加载字体;消除网页字体相关的布局变化。另一方面,如果这些网页字体对用户体验至关重要,你仍然希望尽快加载它们。将预加载与 font-display: block; 也许是可以接受的妥协。

  • 在使用字体显示的后备值时预加载字体。 回退是交换和阻塞之间的折衷,因为它的阻塞时间极短。

  • 在没有预加载的情况下使用 font-display 的可选值。 如果 Web 字体对用户体验并不重要,但仍用于呈现大量页面文本,请考虑使用可选值。 在不利条件下, optional 将在后台加载字体以进行下一次导航时以备用字体显示页面文本。 在这些情况下的最终结果是改进了 CLS,因为系统字体将立即呈现,而随后的页面加载将立即加载字体而不会发生布局变化。

当涉及到网络字体时,CLS 是一个难以优化的指标。 与往常一样,在实验室中进行实验,但请相信您的现场数据,以确定您的字体加载策略是在改善 CLS 还是使其变得更糟。

响应指标

First Input Delay (FID) 和与 Interaction to Next Paint 是衡量对用户输入的响应能力的两个指标。由于 Web 上大部分的交互性是由 JavaScript 驱动的,因此预加载支持重要交互的 JavaScript 可能有助于将 FID 和 INP 指标保持在尽可能低的水平。但是,请注意 FID 是一个负载响应指标,INP 观察整个页面生命周期中的交互,包括在启动期间。如果太多资源争用带宽,则在启动期间预加载过多 JavaScript 可能会带来意想不到的负面后果。 您还需要注意如何进行代码拆分。代码拆分是减少启动期间加载的 JavaScript 数量的绝佳优化,但如果它们依赖于在交互开始时加载的 JavaScript,则交互可能会延迟。为了弥补这一点,您需要检查用户的意图,并在交互发生之前为必要的 JavaScript 块注入预加载。一个示例可能是当表单中的任何字段被聚焦时,预加载验证表单内容所需的 JavaScript。

结论

为了提高页面速度,预加载浏览器发现较晚的重要资源。 预加载所有内容会适得其反,因此请谨慎使用预加载并衡量在现实世界中的影响 - measure the impact in the real-world