Codelab:预加载关键资源以提高加载速度

在此 Codelab 中,通过预加载和预提取一些资源,以下网页的性能得以提升:

应用屏幕截图

测量

在添加任何优化措施之前,请先衡量网站的表现。

  • 如需预览网站,请按查看应用,然后按全屏 全屏

对当前版本的 Glitch 运行 Lighthouse 性能审核(Lighthouse > 选项 > 性能),另请参阅使用 Lighthouse 发现性能优化建议

Lighthouse 显示延迟提取的资源存在以下失败的审核结果:

Lighthouse:预加载密钥请求审核
  • 按 `Control+Shift+J`(在 Mac 上,则按 `Command+Option+J`)打开开发者工具。
  • 点击网络标签页。
包含延迟发现的资源的网络面板

放置在 HTML 文档中的 Link 元素 (<link>) 不会提取 main.css 文件,但单独的 JavaScript 文件 fetch-css.js 会在 window.onLoad 事件之后将 Link 元素附加到 DOM。这意味着,只有在浏览器完成解析和执行 JS 文件之后才会提取该文件。同样,只有在 CSS 文件下载完成后,系统才会提取 main.css 中指定的网页字体 (K2D.woff2)。

关键请求链表示浏览器为确定优先级并提取的资源的顺序。对于此网页,它目前如下所示:

├─┬ / (initial HTML file)
  └── fetch-css.js
    └── main.css
      └── K2D.woff2

由于 CSS 文件位于请求链的第三级,因此 Lighthouse 将其标识为延迟发现的资源。

预加载关键资源

main.css 文件是一项关键资源,需要在页面加载后立即使用。对于在您的应用中延迟提取的重要文件(例如此资源),请使用链接预加载标记,通过向文档标头添加 Link 元素来告知浏览器提早下载该文件。

为此应用添加预加载代码:

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
</head>

as 属性用于标识正在提取的资源类型,as="style" 用于预加载样式表文件。

重新加载应用并查看开发者工具中的 Network 面板。

包含预加载资源的网络面板

请注意,在负责提取 CSS 文件的 JavaScript 尚未完成解析之前,浏览器是如何提取该文件的。启用预加载后,浏览器会假定提前提取资源,并假定这对网页至关重要。

如果使用不当,预加载可能会对未使用的资源发出不必要的请求,从而影响性能。在此应用中,details.css 是位于项目根目录下的另一个 CSS 文件,但用于单独的 /details route。如需通过示例显示如何正确使用预加载,请同时为此资源添加预加载提示。

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
  <link rel="preload" href="details.css" as="style">
</head>

重新加载应用并查看 Network 面板。 已发出检索 details.css 的请求,即使网页并未使用它。

带有不必要的预加载的网络面板

如果预加载的资源在加载后的几秒钟内未被网页使用,Chrome 会在 Console 面板中显示一条警告。

控制台中的预加载警告

您可以使用此警告作为指示,以确定您的网页是否有任何未立即使用的预加载资源。您现在可以移除此页面不必要的预加载链接。

<head>
  <!-- ... -->
  <link rel="preload" href="main.css" as="style">
  <link rel="preload" href="details.css" as="style">
</head>

如需查看可提取的所有资源类型的列表,以及应该用于 as 属性的正确值,请参阅关于预加载的 MDN 文章

预提取未来的资源

预提取是另一个浏览器提示,可用于请求用于其他导航路线的资源,但资源的优先级低于当前页面所需的其他重要资源。

在此网站中,点击图片即可转到单独的 details/ 路由。

详情路由

单独的 CSS 文件 details.css 包含这个简单页面所需的所有样式。向 index.html 添加 link 元素即可预提取此资源。

<head>
  <!-- ... -->
  <link rel="prefetch" href="details.css">
</head>

如需了解这如何触发文件请求,请在开发者工具中打开网络面板,然后取消选中停用缓存选项。

在 Chrome 开发者工具中停用缓存

请重新加载应用,并注意在提取了所有其他文件后,系统如何为 details.css 发出了优先级极低的请求。

包含预提取资源的网络面板

打开开发者工具后,点击网站上的图片以转到 details 页面。由于在 details.html 中使用 link 元素提取 details.css,因此系统会按预期发出对资源的请求。

详情页面网络请求

点击开发者工具中的 details.css 网络请求可查看其详细信息。您会发现,系统会从浏览器的磁盘缓存中检索该文件。

从磁盘缓存中提取的详细信息请求

预提取会利用浏览器空闲时间,提前请求其他网页所需的资源。这可以允许浏览器更快地缓存资源,并在需要时从缓存中提供资源,从而加快未来的导航请求。

使用 webpack 进行预加载和预提取

通过代码拆分减少 JavaScript 载荷一文探讨了如何使用动态导入将 bundle 拆分为多个区块。我们通过一个简单的应用演示了这一点,该应用在提交表单时会从 Lodash 动态导入模块。

演示代码拆分的 Magic Sorter 应用

您可以点击此处访问此应用的故障提示。

以下代码块(位于 src/index.js, 中)负责在用户点击按钮时动态导入该方法。

form.addEventListener("submit", e => {
  e.preventDefault()
  import('lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

拆分 bundle 可以缩减其初始大小,从而缩短网页加载时间。webpack 4.6.0 版本支持预加载或预提取动态导入的分块。以此应用为例,可以在浏览器空闲时预提取 lodash 方法;当用户按下该按钮时,无需延迟即可提取资源。

在动态导入中使用特定的 webpackPrefetch 注释参数来预提取特定分块。使用这一特定应用时,其外观如下所示。

form.addEventListener("submit", e => {
  e.preventDefault()
  import(/* webpackPrefetch: true */ 'lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

应用重新加载后,Webpack 会将资源的预提取标记注入文档标头中。可以在开发者工具的元素面板中看到这一点。

包含预提取标记的“元素”面板

观察 Network 面板中的请求还会发现,在请求所有其他资源之后,系统会以较低的优先级提取此分块。

包含预提取请求的“网络”面板

虽然预提取更适合此用例,但 webpack 还支持预加载动态导入的分块。

import(/* webpackPreload: true */ 'module')

总结

通过此 Codelab,您应该能够深入了解预加载或预提取特定资源如何改善网站的用户体验。请务必注意,这些技术不应用于所有资源,错误使用它们可能会降低性能。仅有选择地预加载或预提取即可获得最佳结果。

总结:

  • 针对延迟发现但对当前网页至关重要的资源使用预加载
  • 对未来导航路线或用户操作所需的资源使用预提取功能。

目前,并非所有浏览器都支持预加载和预提取。这意味着,并非所有应用用户都会发现性能有所提升。

如需详细了解预加载和预提取对网页的影响的具体方面,请参阅以下文章: