当您打开网页时,浏览器会向服务器请求 HTML 文档,解析其内容,并针对任何引用的资源提交单独的请求。作为开发者,您已经了解网页需要的所有资源,以及哪些资源最重要。您可以利用这些知识提前请求关键资源,从而加快加载流程。本文介绍了如何使用 <link rel="preload">
实现此目的。
预加载的工作原理
预加载最适合通常由浏览器较晚发现的资源。
通过预加载某个资源,您是在告知浏览器您希望在浏览器发现该资源之前提前提取该资源,因为您确定该资源对当前网页很重要。
关键请求链表示浏览器优先提取资源的顺序。Lighthouse 会将位于此链第三级的资源标记为“最近发现”。您可以使用预加载密钥请求审核来确定要预加载哪些资源。
您可以通过向 HTML 文档的标头中添加带有 rel="preload"
的 <link>
标记来预加载资源:
<link rel="preload" as="script" href="critical.js">
浏览器会缓存预加载的资源,以便在需要时立即使用。(它不会执行脚本或应用样式表。)
资源提示(例如 preconnect
和 prefetch
)会根据浏览器的判断执行。另一方面,preload
对浏览器来说是必需的。现代浏览器在确定资源优先级方面已经非常出色,因此请务必谨慎使用 preload
,并仅预加载最重要的资源。
未使用的预加载内容会在 load
事件发生后大约 3 秒内在 Chrome 中触发控制台警告。
使用场景
预加载 CSS 中定义的资源
只有在浏览器下载并解析这些 CSS 文件后,才会发现使用 @font-face
规则定义的字体或在 CSS 文件中定义的背景图片。预加载这些资源可确保在 CSS 文件下载之前提取这些资源。
预加载 CSS 文件
如果您使用的是关键 CSS 方法,则需要将 CSS 拆分为两部分。用于渲染首屏内容的关键 CSS 会内嵌在文档的 <head>
中,而非关键 CSS 通常会使用 JavaScript 延迟加载。在加载非关键 CSS 之前等待 JavaScript 执行可能会导致用户滚动时渲染延迟,因此最好使用 <link rel="preload">
更早地发起下载。
预加载 JavaScript 文件
由于浏览器不会执行预加载的文件,因此预加载有助于将提取与执行分离开来,这有助于提升“互动所需时间”等指标。如果您拆分 JavaScript 软件包,并仅预加载关键代码块,则预加载效果最佳。
如何实现 rel=preload
实现 preload
的最简单方法是向文档的 <head>
添加 <link>
标记:
<head>
<link rel="preload" as="script" href="critical.js">
</head>
提供 as
属性有助于浏览器根据资源类型设置预提取资源的优先级、设置正确的标头,以及确定资源是否已存在于缓存中。此属性接受的值包括:script
、style
、font
、image
和其他。
某些类型的资源(例如字体)会以匿名模式加载。对于这些设备,您必须使用 preload
设置 crossorigin
属性:
<link rel="preload" href="ComicSans.woff2" as="font" type="font/woff2" crossorigin>
<link>
元素还接受 type
属性,其中包含关联资源的 MIME 类型。浏览器使用 type
属性的值来确保仅在资源的文件类型受支持时才预加载资源。如果浏览器不支持指定的资源类型,则会忽略 <link rel="preload">
。
您还可以通过 Link
HTTP 标头预加载任何类型的资源:
Link: </css/style.css>; rel="preload"; as="style"
在 HTTP 标头中指定 preload
的好处在于,浏览器无需解析文档即可发现它,这在某些情况下可以带来一些改进。
使用 webpack 预加载 JavaScript 模块
如果您使用的是用于创建应用 build 文件的模块捆绑器,则需要检查它是否支持注入预加载代码。使用 webpack 4.6.0 或更高版本时,可以通过在 import()
中使用魔法注释来支持预加载:
import(_/* webpackPreload: true */_ "CriticalChunk")
如果您使用的是旧版 webpack,请使用第三方插件,例如 preload-webpack-plugin。
预加载对 Core Web Vitals 的影响
预加载是一项强大的性能优化措施,会影响加载速度。此类优化可能会导致您网站的核心 Web 指标发生变化,因此请务必留意。
Largest Contentful Paint (LCP)
在字体和图片方面,预加载对 Largest Contentful Paint (LCP) 有显著影响,因为图片和文本节点都可能是 LCP 候选项。使用 Web 字体呈现的主打图片和大量文本可以通过恰当放置的预加载提示显著受益,因此,当有机会更快地向用户传送这些重要内容时,应使用预加载提示。
不过,在预加载和其他优化方面,您需要格外小心!尤其要避免预加载过多资源。如果优先级过多,实际上就没有任何资源具有优先级。过多预加载提示的影响对网络速度较慢的设备尤为不利,因为带宽争用会更加明显。
而是应着重考虑一些您知道通过恰当的预加载会受益的高价值资源。预加载字体时,请确保以 WOFF 2.0 格式提供字体,以尽可能缩短资源加载时间。由于 WOFF 2.0 具有出色的浏览器支持,因此如果 LCP 候选项是文本节点,使用 WOFF 1.0 或 TrueType (TTF) 等旧版格式会延迟 LCP。
在 LCP 和 JavaScript 方面,您需要确保从服务器发送完整的标记,以便浏览器的预加载扫描器正常运行。如果您提供的体验完全依赖于 JavaScript 来呈现标记,并且无法发送服务器呈现的 HTML,则可以介入浏览器预加载扫描器无法处理的情况,并预加载那些只有在 JavaScript 加载和执行完毕后才能发现的资源。
Cumulative Layout Shift (CLS)
累积布局偏移 (CLS) 是与网页字体相关的特别重要的指标,并且 CLS 与使用 font-display
CSS 属性来管理字体加载方式的网页字体之间存在显著的相互作用。如需最大限度地减少与 Web 字体相关的布局偏移,请考虑以下策略:
- 预加载字体,同时为
font-display
使用默认block
值。这是一种微妙的平衡。禁止显示没有后备字体的字体可以被视为用户体验问题。一方面,使用font-display: block;
加载字体可消除与 Web 字体相关的布局偏移。另一方面,如果这些 Web 字体对用户体验至关重要,您仍然希望尽快加载这些字体。将预加载与font-display: block;
结合使用可能是可接受的折衷方案。 - 预加载字体,同时为
font-display
使用fallback
值。fallback
是swap
和block
之间的折衷方案,因为它的屏蔽期非常短。 - 为
font-display
使用optional
值,不进行预加载。如果某个 Web 字体对用户体验不是至关重要,但仍用于渲染大量网页文本,请考虑使用optional
值。在恶劣条件下,optional
会使用后备字体显示网页文本,同时在后台加载字体以供下次导航。在这些情况下,最终结果是 CLS 会得到改善,因为系统字体会立即呈现,而后续的网页加载会立即加载字体,而不会出现布局偏移。
在网页字体方面,CLS 是一项难以优化的指标。一如既往,请在实验室中进行实验,但要依靠现场数据来确定您的字体加载策略是改善了 CLS 还是使其变得更糟。
Interaction to Next Paint (INP)
Interaction to Next Paint 是一项衡量对用户输入的响应速度的指标。由于 Web 上互动性的绝大部分是由 JavaScript 驱动的,因此预加载用于支持重要互动的 JavaScript 可能会有助于降低网页的 INP。不过,请注意,如果有太多资源在争用带宽,在启动期间预加载过多的 JavaScript 可能会带来意想不到的负面影响。
您还需要注意如何进行代码分块。代码拆分是一项非常有效的优化,可减少启动期间加载的 JavaScript 量,但如果互动依赖于在互动开始时加载的 JavaScript,则可能会出现互动延迟。为了弥补这一点,您需要检查用户的意图,并在互动发生之前为必要的 JavaScript 分块注入预加载。例如,在表单中的任何字段获得焦点时,预加载验证表单内容所需的 JavaScript。
总结
为了提高网页速度,请预加载浏览器较晚发现的重要资源。预加载所有内容会适得其反,因此请谨慎使用 preload
,并衡量实际影响。