关于 preload
面向 Web 开发者的性能优化指南,介绍何时及如何预加载关键资源,并说明资源类型、CORS 和优先级方面的注意事项。
主要功能
- 01提前请求延迟发现的关键资源
- 02为脚本、样式、字体和图片设置类型
- 03使字体预加载与 CORS 请求匹配
- 04权衡加载优先级与页面渲染
适用场景
- 01加快首屏关键字体或图片加载
- 02提前加载交互所需的 JavaScript
- 03优化单页应用中即将显示的组件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ patternsdev/ skills --skill preload安装后在 Claude Code 中输入/preload确认已加载。
详细介绍
预加载
目录
预加载(<link rel="preload">)是一种浏览器优化,可提前请求关键资源(这些资源可能较晚才会被发现)。如果你熟悉如何手动安排关键资源的加载顺序,它可以对加载性能以及 Core Web Vitals 指标产生积极影响。不过,预加载并非万能方案,使用时需要了解其中的一些权衡。
何时使用
- 当关键资源(字体、脚本、图片)在加载过程中较晚才被发现时使用
- 有助于改善交互时间(TTI)和最大内容绘制(LCP)
何时不使用
- 对于非关键资源,不要使用——预加载过多资源会延迟真正影响初始渲染的资源
- 当浏览器的预加载扫描器已经较早发现资源时(例如
<head>中的内联<script>标签) - 过度使用导致浏览器警告预加载的资源未被使用时,这表明带宽被浪费
说明
- 对当前页面立即需要的资源使用
<link rel="preload"> - 注意不要因预加载过多资源而延迟首次内容绘制
- 使用
as属性指定资源类型(script、style、font、image) - 对字体和其他通过 CORS 获取的资源,在预加载中设置
crossorigin,使其与最终请求模式一致 - 仅预加载初始渲染后约 1 秒内必须可见的资源
详情
<link rel="preload" href="emoji-picker.js" as="script">
...
</head>
<body>
...
<script src="stickers.js" defer></script>
<script src="video-sharing.js" defer></script>
<script src="emoji-picker.js" defer></script>
在针对交互时间或首次输入延迟等指标进行优化时,预加载有助于加载实现交互所需的 JavaScript 包(或代码块)。请注意,使用预加载时务必谨慎:你需要避免为了改善交互性而延迟加载首次内容绘制或最大内容绘制所需的资源(例如主视觉图片或字体)。
如果你想优化第一方 JavaScript 的加载,也可以考虑在文档的 <head> 中使用 <script defer>,而不是放在 <body> 中,以便更早发现这些资源。
在单页应用中预加载
预取 是缓存近期可能会请求的资源的好方法,而对于需要立即使用的资源,我们可以进行 预加载。可能是初次渲染时使用的某种字体,也可能是用户一开始就能看到的某些图片。
假设我们的 EmojiPicker 组件应该在初次渲染时立即显示。虽然它不应该包含在主捆绑包中,但它 应该 并行加载。和 预取 一样,我们可以添加一条魔法注释,让 Webpack 知道这个模块应该预加载。
const EmojiPicker = import(/* webpackPreload: true */ "./EmojiPicker");
Webpack 4.6.0+ 支持通过在导入中添加
/* webpackPreload: true */来预加载资源。要在较旧版本的 webpack 中使用预加载功能,需要将preload-webpack-plugin添加到 webpack 配置中。
构建应用后,我们可以看到 EmojiPicker 会被预加载。
Asset Size Chunks Chunk Names
emoji-picker.bundle.js 1.49 KiB emoji-picker [emitted] emoji-picker
vendors~emoji-picker.bundle.js 171 KiB vendors~emoji-picker [emitted] vendors~emoji-picker
main.bundle.js 1.34 MiB main [emitted] main
Entrypoint main = main.bundle.js
(preload: vendors~emoji-picker.bundle.js emoji-picker.bundle.js)
实际输出会显示为文档 head 中带有 rel="preload" 的 link 标签。
<link rel="preload" href="emoji-picker.bundle.js" as="script" />
<link rel="preload" href="vendors~emoji-picker.bundle.js" as="script" />
预加载的 EmojiPicker 可以与初始捆绑包并行加载。与 prefetch 不同,浏览器仍会自行判断网络连接和带宽是否足以进行实际预取;预加载 的资源则无论如何都会被预加载。
我们不必等到初次渲染后才加载 EmojiPicker,而是可以立即使用该资源!由于我们以更智能的顺序加载资源,初始加载时间可能会显著增加,具体取决于用户的设备和网络连接。只预加载那些必须在初次渲染后约 1 秒内显示的资源。
预加载 + async 技巧
如果希望浏览器以高优先级下载脚本,但又不阻塞解析器等待脚本,可以使用下面的预加载 + async 技巧。在这种情况下,预加载可能会延迟其他资源的下载,这是开发者必须做出的权衡:
<link rel="preload" href="emoji-picker.js" as="script">
<script src="emoji-picker.js" async>
字体预加载必须使用 crossorigin
字体会作为 CORS 资源获取,即使它们托管在同源服务器上也是如此。这意味着预加载请求和后续的 @font-face 请求必须使用相同的获取模式,否则就无法复用预加载结果。
如果预加载字体时 没有 使用 crossorigin,浏览器通常会发出 no-cors 预加载请求;之后 CSS 发现该字体时,又会单独发出 cors 请求。这会导致同一个文件被获取两次,浪费带宽。
避免:
<link rel="preload" href="/fonts/inter-roman.woff2" as="font" type="font/woff2">
建议:
<link
rel="preload"
href="/fonts/inter-roman.woff2"
as="font"
type="font/woff2"
crossorigin
>
并确保 @font-face 使用相同的资源:
@font-face {
font-family: "Inter";
src: url("/fonts/inter-roman.woff2") format("woff2");
font-display: swap;
}
更广泛地说,同样的规则也适用:如果后续使用方以 CORS 语义获取资源,预加载也应匹配相同的模式。
Chrome 95+ 中的预加载
得益于 Chrome 95+ 对预加载插队行为的一些修复,这项功能在更广泛的场景下使用时更加安全。Chrome 的 Pat Meenan 对预加载提出了以下新建议:
- 将其放在 HTTP 标头中,会使其排在所有其他资源之前
- 一般来说,对于优先级 >= Medium 的资源,预加载会按照解析器遇到它们的顺序加载,因此要谨慎将预加载项放在 HTML 开头。
- 字体预加载最好放在 head 的末尾或 body 的开头
- 导入预加载应放在需要该导入的 script 标签之后(这样实际脚本会先加载并解析)
- 图片预加载的优先级较低,应根据 async 脚本和其他优先级为 low/lowest 的标签安排顺序
结论
再次提醒,谨慎使用 preload,并在生产环境中衡量其影响。如果图片的 preload 声明在文档中的位置比现在更靠前,这有助于浏览器发现该图片(并确定它相对于其他资源的加载顺序)。错误使用 preload 可能会导致图片延迟首次内容绘制(例如 CSS、字体)——这与预期正好相反。另请注意,要让这类优先级调整有效,还取决于服务器是否正确地为请求排序。
在需要获取脚本但不执行它们时,你也可能会发现 <link rel="preload"> 很有用。
web.dev 上的多篇文章介绍了如何使用 Preload 来: