30 seconds of code 之浏览器资源预取:用 rel="prefetch" 与 rel="preload" 提升感知加载速度

原创2026-09-30 20:12:43711 阅读
文章标签:教程文档

30 seconds of code 之浏览器资源预取:用 rel="prefetch" 与 rel="preload" 提升感知加载速度

资源预取(resource prefetching)是一种投入极小、回报明显的性能优化手段:它利用浏览器空闲时间提前下载后续可能用到的资源并写入缓存,从而显著改善网站的感知加载速度(perceived page speed)与用户体验。本篇指南以 30 seconds of code 仓库中的 Prefetching resources in the browser 一文为骨架,结合仓库内 HTML 集合的相关文章,系统讲解 rel="prefetch"、rel="preload" 等资源提示(resource hints)的用法、区别、适用场景与进阶方案。读完你将掌握如何用几行 <link> 标签为页面注入可感知的性能提升,并理解预取与预加载、预渲染之间的取舍。

为什么需要资源预取

现代网页由大量外部资源组成——脚本、样式表、图片、字体等,它们都会影响页面的加载时间。性能优化通常围绕"减少请求数"与"缩短等待时间"展开,而资源预取走的是另一条路:与其让用户在点击后才开始等待下载,不如提前把"很可能需要"的资源准备好。

预取的核心机制是利用浏览器空闲时间。浏览器会在主渲染流程完成、网络空闲的间隙,按照 <link> 标签给出的提示去获取资源,因此:

  • 它不会拖慢首屏加载,因为预取发生在空闲时段;
  • 它提升后续页面的打开速度,因为资源已提前进入缓存;
  • 它对字体(webfonts)等特殊资源收益尤其大——字体通常在浏览器下载、解析并渲染完 DOM 之后才开始下载,预取可以让字体下载尽早开始,避免文字闪烁(FOUT)与布局抖动。

在仓库的 HTML 文章集合中,这一主题与 Lazy load images in HTML(懒加载)、Recommended minimum HTML head(<head> 元数据规范)共同构成了一套完整的 HTML 性能优化知识体系:懒加载是"延迟加载不可见资源",预取则是"提前加载即将需要的资源"。

rel="prefetch":给浏览器一个提示

预取资源的写法非常简单——在文档的 <head> 或 <body> 中放入一个带 rel="prefetch" 属性的 <link> 标签即可:

<link rel="prefetch" href="./cool_page/data.json">

这条标签的作用是向浏览器发出提示:我们稍后会用到这个资源,请在空闲时下载并存入缓存。需要注意,预取本质上是一个 hint(提示) 而非强制指令,因此浏览器在某些情况下可能会忽略它,例如:

  • 浏览器正在进行其他下载任务,网络带宽被占用;
  • 用户设备或浏览器设置了省流量、节电等相关策略;
  • 网络连接质量不佳或出现异常。

这并不意味着预取不可靠,而是提醒开发者:预取是"锦上添花"的优化,不应把业务正确性建立在"它一定会执行"之上。

关于预取的协议与同源限制,有以下两点值得明确:

  • 目前资源预取只支持 http:// 和 https:// 协议的链接,其他协议(如 file:、data:)无法通过该机制预取;
  • 同源限制不适用于资源预取——也就是说,即使资源位于其他域名,只要 URL 合法,浏览器同样可以预取。这为加载 CDN 上的第三方资源、公共 API 数据提供了便利。

rel="preload":让浏览器必须加载

与"提示性"的 prefetch 相对,rel="preload" 是指令性(directive) 的:它不是建议,而是要求浏览器尽快下载并缓存指定资源。写法与预取几乎一致:

<link rel="preload" href="./important_page/data.json">

preload 适合当前页面马上就要用到的关键资源(如首屏渲染所需的字体、图片或脚本),它让浏览器尽早发起请求,而不是等到解析到该资源时才下载。两者的核心差异可以总结为:

属性 语义 适用时机 浏览器是否可忽略
rel="prefetch" 提示:空闲时下载,供后续页面使用 用户即将访问的下一页所需资源 是(hint)
rel="preload" 指令:尽快下载,供当前页面使用 当前页面几秒内就会用到的关键资源 否(必须执行)

原文档同时提醒:preload 相比 prefetch 唯一的短板是浏览器支持度可能较低(具体取决于读者阅读文章时的浏览器版本与发布时间,该文标注于 2021 年)。因此在使用 preload 时,建议关注目标用户群体的浏览器分布,并始终让页面在资源提示未被支持时仍能正常工作。

资源加载 rel 值速查表:从 preload 到 modulepreload

仓库中的姊妹篇 Resource Preloading Cheat Sheet 把 <link> 的 rel 属性在资源加载场景下的常用取值整理成了一张速查表,方便按需取用:

  • rel="preload":尽快下载资源。用于当前页面几秒后就会用到的资源。
  • rel="prefetch":提前获取资源(作为建议)。用于下一个页面会用到、当前页面用不到的资源。
  • rel="preconnect":提前建立到目标站点的连接(含 DNS 解析、TCP 握手、TLS 协商),以加速后续从该站点拉取资源。用于"确定要使用某站资源、但还不知道完整 URL"的场景,例如站外字体、CDN 资源。
  • rel="dns-prefetch":提前解析目标站点的 DNS,以加速后续请求。使用场景与 preconnect 相同,主要用于对 preconnect 支持度较差的旧浏览器做降级补充。
  • rel="prerender":预加载并在后台渲染整个页面,使未来导航几乎瞬时完成。用于用户极有可能访问的特定页面。
  • rel="modulepreload":预加载一个 JavaScript 模块脚本。用于即将需要某个 ES Module 脚本的场景。

这张表给出了同一优化目标下从"轻量"到"重量"的完整手段谱系:dns-prefetch 只解析域名 → preconnect 建立连接 → prefetch 下载资源 → prerender 连页面一起渲染。开发者可以根据资源的重要程度和确定性,选择不同力度的优化,避免不必要的资源消耗。

一个完整的组合示例(放在 <head> 中)如下:

<head>
  <!-- 关键字体:当前页面立刻需要,使用指令式预加载 -->
  <link rel="preload" href="./fonts/main.woff2" as="font" type="font/woff2" crossorigin>
  <!-- 下一页数据:提示性预取 -->
  <link rel="prefetch" href="./cool_page/data.json">
  <!-- 站外 CDN:提前建立连接 -->
  <link rel="preconnect" href="https://cdn.example.com">
  <!-- 旧浏览器降级:只解析 DNS -->
  <link rel="dns-prefetch" href="https://cdn.example.com">
</head>

补充说明:preload 在用于字体等资源时通常还需配合 as 属性(声明资源类型,帮助浏览器设定优先级与同源策略)与 crossorigin 属性,这也是速查表之外值得查阅官方规范的细节。仓库内关于 <head> 元数据组织的更多实践,可参考 Recommended minimum HTML head 与 Recommended HTML head links。

把预取放进文档的正确位置

预取标签既可以放在 <head> 中,也可以放在 <body> 中,两者的作用没有本质区别。但从工程习惯与可维护性出发,资源提示通常与页面元数据一起集中放在 <head> 里。

仓库的 Recommended minimum HTML head 一文强调,<head> 是存放文档元数据(字符编码、视口、标题、描述)以及外部资源链接的标准位置:

<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Page Title</title>
  <meta name="description" content="Page description. No longer than 155 characters.">
  <!-- 在此处追加资源预取 / 预加载标签 -->
</head>

将 prefetch、preload、preconnect 等标签统一收纳在 <head> 的元数据区域,既符合 HTML 规范习惯,也便于后期审计"这个页面到底预取了哪些资源"。

进阶:从逐条预取到规则化预取(Speculation Rules API)

手工为每个链接写一条 <link rel="prefetch"> 是可行但繁琐的——尤其是当站点页面很多、需要判断哪些链接值得预取时。仓库在 A first look at the Speculation Rules API 中介绍了更进一步的方案:Speculation Rules API。

该 API 允许开发者用一段 type="speculationrules" 的 <script> 标签配合 JSON 规则,声明"什么时候、以什么方式预取哪些资源",让浏览器根据用户行为(如鼠标悬停、聚焦、点击)自适应地触发预取,从而免去逐条维护 <link> 的负担。例如:

<!-- 以中等急切度预取所有本地页面 -->
<script type="speculationrules">
{
  "prefetch": [{
    "where": {
      "href_matches": "/*"
    },
    "eagerness": "moderate"
  }]
}
</script>

规则还可以结合 URL Pattern API 做更精细的匹配与排除(如排除后台管理页、购物车链接、rel~=nofollow 链接),甚至用 "prerender" 替代 "prefetch" 在后台直接渲染整个页面。当然,该 API 目前仍处于实验阶段,使用前务必查阅官方文档并关注浏览器支持情况。

注意事项与最佳实践

无论使用 prefetch、preload 还是 Speculation Rules API,都需要牢记以下原则:

  1. 预取不是免费的:每一次预取都对应一次真实的 HTTP 请求,会消耗网络带宽;过度预取反而可能在低端设备或慢速网络上拖慢性能。
  2. 只预取"很可能需要"的资源:根据用户浏览行为判断,优先预取用户大概率访问的下一页;规则宁可严格一些,也不要激进到把用不上的资源全部下载下来。
  3. 预加载比预取更昂贵:preload(以及更极端的 prerender)会抢占带宽并占用内存;在多数情况下,什么都不预加载,也好过预加载了却用不上。
  4. 把它当作渐进增强:资源提示本身不被支持或被执行时,页面应仍然可用——它们只是性能加速器,而不是功能依赖。

总而言之,浏览器资源预取是一个"性价比"极高的优化手段:几行 <link> 标签即可利用空闲网络带宽换取明显的感知速度提升。从 dns-prefetch、preconnect、prefetch 到 preload、prerender、modulepreload,开发者可以按需选用不同力度的方案;更进一步,还可以借助 Speculation Rules API 将预取规则化、行为化。要系统掌握这套知识,建议在本仓库中继续阅读 Resource Preloading Cheat Sheet、Speculation Rules API first look 与 Lazy load images in HTML,将"预取"与"懒加载"组合使用,构建完整的前端加载性能优化策略。

登录后查看全文
30-seconds-of-code