Web Performance

Preload、prefetch 和 preconnect:它们何时真正有用

Resource hints 只有在匹配真实的浏览器瓶颈时才有用。盲目使用会增加优先级噪声,有时还会让页面更慢。

The Wux Webtools Team The Wux Webtools Team 10 分钟阅读 人工智能辅助,人工审核
A simplified browser loading waterfall showing early resource hints for a web page.
目录
  1. Resource hints 不是魔法
  2. 浏览器本来就做得很好的事
  3. Preload:用于当前页面中发现太晚的资源
  4. Preload 与 LCP images
  5. Prefetch:用于下一页,而不是当前页
  6. Preconnect:用于连接成本高的重要 origins
  7. DNS-prefetch:更轻量的近亲
  8. 如何决策:一个实用工作流
  9. 1. 识别瓶颈
  10. 2. 一次只添加一个 hint
  11. 3. 检查优先级副作用
  12. 4. 验证 headers 和 caching
  13. 常见错误
  14. Preload 太多
  15. 对必需资源使用 prefetch
  16. 对每个第三方都 preconnect
  17. 忘记移动端条件
  18. 一个简单决策表
  19. 平静的规则

Resource hints 不是魔法

preloadprefetchpreconnect 常常被当作性能检查清单来对待。往 <head> 里加几个标签,重新跑一次 Lighthouse,然后感觉好多了。它们并不是这样工作的。

这些 hints 是给浏览器加载管线的指令。当你知道某些浏览器无法足够早发现的信息时,它们可以帮上忙。当你只是猜测、过度提升非关键工作的优先级,或为用户根本不会需要的连接预热时,它们也可能造成伤害。

简短版本:

  • 对当前页面需要、但发现得太晚的资源使用 preload
  • 对很可能用于未来导航的资源使用 prefetch,而不是当前页面的必要资源。
  • 对连接建立确实会造成延迟的重要第三方 origin 使用 preconnect

实际问题不是“哪种 hint 最快?”而是“浏览器在等什么,而这个 hint 能不能消除这段等待?”

浏览器本来就做得很好的事

现代浏览器不是被动的文件下载器。它们会解析 HTML、提前扫描资源、分配优先级、复用连接、延迟不可见的工作,并根据网络条件进行调整。

这意味着 resource hints 应该有选择地使用。如果某个 stylesheet、script、image 或 font 已经被很早发现,并获得了正确优先级,那么添加 hint 可能毫无作用。更糟的是,它还可能与更重要的资源竞争。

在添加 hints 之前,先查看 DevTools 中的 waterfall trace 或实验室报告。如果你使用 Lighthouse,先从 diagnostics 开始,而不是盯着分数;我们有一篇单独的指南,讲如何不慌不忙地阅读 Lighthouse report —— 但请注意,正确的 URL 区分大小写,所以如有需要,请从你站点导航中的链接文章进入。

真正的证据通常可以在三个地方看到:

  1. 某个关键资源开始得很晚,因为浏览器发现它太晚。
  2. 到某个重要 origin 的连接在第一个请求之前花了明显时间。
  3. 下一页资源高度可预测,并且适合在空闲时间低成本获取。

如果这些都不成立,那么 hint 很可能只是装饰。

Preload:用于当前页面中发现太晚的资源

preload 告诉浏览器:“现在就获取这个资源,因为当前页面会用到它。”

一个典型例子是 CSS 中引用的 web font。浏览器必须下载 HTML、发现 CSS、下载 CSS、解析 CSS、发现 font,然后再请求 font。如果这个 font 对首屏文本很重要,发现时间可能晚到足以造成布局偏移或文本渲染延迟。

preload 可以把这个请求提前:

<link rel='preload' href='/fonts/inter-var.woff2' as='font' type='font/woff2' crossorigin>

as 属性很重要。它告诉浏览器这是什么类型的资源,从而影响优先级、缓存、内容安全策略和请求头。Fonts 通常还需要 crossorigin,即使它们来自同一站点,因为 font 获取使用 CORS 模式。

好的 preload 候选包括:

  • 用于可见文本的主要 web font。
  • 作为 Largest Contentful Paint 元素、且无法被早期发现的 hero image。
  • 间接加载的关键 CSS 文件。
  • 很早就需要、但隐藏在另一个 script 后面的 module 或 script。

不好的 preload 候选包括:

  • 设计系统中的每一种 font weight。
  • 首屏以下的 images。
  • 初始渲染不需要的 scripts。
  • 浏览器已经能在第一个 HTML chunk 中发现的资源。

Preload 很强大,因为它会影响当前页面的优先级。这也正是它容易被误用的原因。如果你 preload 了五个大型资源,你就不再是在帮助浏览器,而是在和它争辩。

Fonts 是经典案例。Preload 一个主要 font 文件可能有帮助。Preload 六种字重和斜体通常会让情况更糟。如果 fonts 是你的瓶颈,先修整 font 集;我们的指南 为什么 web fonts 仍然是大多数站点上最容易获得的性能收益 更详细地介绍了这种清理工作。

Preload 与 LCP images

当 LCP image 没有出现在初始 HTML 中时,preload 它可能有用。常见原因包括 CSS background images、client-rendered components,或出现得较晚的 responsive image 逻辑。

但如果你的 hero image 已经作为 <img> 出现在 HTML 中,并带有合理的 srcsetsizes、尺寸,且没有 lazy loading,浏览器很可能可以很快找到它。在这种情况下,根据页面情况,添加 fetchpriority='high' 可能比 preload 更合适。

一个好的测试是:如果 image 请求在 waterfall 中开始得很晚,并成为 LCP 元素,就考虑 preload。如果它开始得很早但下载很慢,问题在于大小、格式、CDN 行为或服务器延迟,而不是发现时机。关于图像格式决策,请参阅 AVIF 何时胜过 WebP,以及何时不会

Prefetch:用于下一页,而不是当前页

prefetch 告诉浏览器:“这个资源可能很快会需要,但现在不是必需的。”

这个区别很重要。Prefetch 的优先级被有意设置得很低。浏览器可能会在空闲时间获取它并存起来供之后使用。它也可能在网络较差、启用省流模式或内存压力较大时跳过它。

当用户意图足够强、使下一个资源很可能被需要时,使用 prefetch。

好的 prefetch 候选包括:

  • 多页 checkout 中的下一步。
  • 用户开始输入查询后,若下一条 route 可预测,则预取搜索结果。
  • 当用户正在阅读附近内容时,目录中链接到的 documentation pages。
  • 单页应用中,用户 hover 或 focus 某个导航项后的 route chunks。

不好的 prefetch 候选包括:

  • 你的整个导航树。
  • 大型 videos 或 image galleries。
  • “以防万一”的 third-party scripts。
  • 用户很少会接着访问的页面。

Prefetch 是克制会带来回报的地方。一个被获取但从未使用的资源并不是免费的。它会消耗带宽、服务器容量、能源,并且可能消耗用户流量。在移动网络上,推测性获取可能非常不友好。

对许多站点来说,最好的 prefetch 策略是基于意图的。不要在 home page 一加载就 prefetch pricing page。等用户打开 pricing menu、hover pricing link,或滚动到强烈预示导航的 call-to-action 附近时再 prefetch。

还要记住,浏览器行为会有所不同。有些浏览器对 prefetch 很保守;某些隐私设置会减少或禁用 speculative loading。把 prefetch 视为一种机会性的改进,而不是保证正确性的机制。

Preconnect:用于连接成本高的重要 origins

preconnect 告诉浏览器:“现在就开始建立到这个 origin 的连接。”

这可能包括 DNS lookup、TCP connection 和 TLS negotiation。对于 third-party origins,这个建立过程可能花费数百毫秒,尤其是在高延迟网络上。如果页面很快需要从该 origin 发起关键请求,preconnect 可以让后续请求更快。

示例:

<link rel='preconnect' href='https://fonts.gstatic.com' crossorigin>

好的 preconnect 候选包括:

  • 用于 render-blocking 文本的 font origin。
  • 初始交互期间需要的关键 API origin。
  • 提供首屏 assets 的 CDN origin。
  • 用户操作后立即需要的 payments 或 identity provider。

不好的 preconnect 候选包括:

  • 对用户不关键的 analytics 和 advertising endpoints。
  • 只在部分 sessions 中使用的 origins。
  • 很长的第三方列表。
  • same-origin 资源,因为浏览器已经有连接,或很快会打开连接。

Preconnect 有持有成本。打开的 sockets 会消耗内存和网络资源。浏览器会关闭未使用的连接,但这并不意味着不必要的 preconnect 是无害的。

一条有用规则:在一个页面上,最多 preconnect 一到两个高置信度的 third-party origins。如果你想添加更多,你的 third-party 架构可能比 hints 扩展更需要审查。

DNS-prefetch:更轻量的近亲

你也可能看到 dns-prefetch

<link rel='dns-prefetch' href='https://example-cdn.com'>

它只解析域名。它不会打开 TCP 或 TLS 连接。它比 preconnect 更便宜,但帮助也更少。

对于置信度较低的 third-party origins,如果完整 preconnect 显得过于激进,DNS-prefetch 可能是合理的。实践中,如果某个 origin 很关键并且很快一定会用到,优先使用 preconnect。如果只是可能会用到,要么使用 DNS-prefetch,要么什么也不做。

如何决策:一个实用工作流

从测量开始,而不是从标签开始。

1. 识别瓶颈

打开 performance trace,寻找发现过晚的情况。font、hero image 或 script 请求是否只有在另一个文件下载并解析后才开始?这就是 preload 候选。

如果某个请求只有在到 third-party origin 的 DNS/TCP/TLS 建立过程很长之后才开始,那就是 preconnect 候选。

如果当前页面没问题,但下一次导航可预测地慢,prefetch 可能有帮助。

2. 一次只添加一个 hint

Resource hints 会相互影响。添加一个,测试它,并且只有当 waterfall 改善且面向用户的指标没有退化时才保留。

对于 preload,观察被 hint 的资源是否真的很快被使用。Chrome 可能会在 preloaded resource 在 load 后不久没有被使用时发出警告。认真对待这个警告。

3. 检查优先级副作用

Preload 可能会从更重要的 CSS、JavaScript 或 images 那里抢走带宽。Preconnect 可能占用一个连接槽位。Prefetch 可能增加后台流量。

正确的结果不是“被 hint 的文件更早开始”。正确的结果是“页面对用户来说有了有意义的改善”。尽可能查看 LCP、INP、CLS 和 real-user monitoring。

4. 验证 headers 和 caching

Hints 可以通过 HTML 或 HTTP Link headers 发送。当服务器很早就知道页面需要什么时,headers 很有用,但它们也更难随手检查。如果你正在调试某个 hint 是否真的存在于生产环境中,原始 headers 很重要;这正是 我们的 redirects 和 HTTP headers 生产环境调试指南 所覆盖的场景。

Caching 也很重要。如果 preload 一个资源时 credentials 不匹配、as 错误,或 URL 参数不同,可能导致重复下载。这是一个出于好意的 preload 变成性能 bug 的最常见方式之一。

常见错误

Preload 太多

如果一切都是关键的,就没有什么是关键的。将 preload 限制在初始渲染或即时交互所需的资源上。一个典型页面应该有零到三个 preloads,而不是二十个。

对必需资源使用 prefetch

Prefetch 是低优先级且可选的。不要把它用于当前页面必需的 assets。如果页面现在就需要它,考虑 preload 或正常的 HTML 发现。

对每个第三方都 preconnect

第三方很多的页面通常有十个或更多 external origins。对它们全部 preconnect 会制造噪声。只选择那一两个既关键又可预测会使用的。

忘记移动端条件

Resource hints 在较慢连接上最有价值,但在那里也最危险。在快速桌面连接上浪费一次 prefetch 只是一个很小的误差。在受限的移动套餐上,这就是糟糕的取舍。

一个简单决策表

| Situation | Best hint | Why | |---|---:|---| | 通过 CSS 发现的关键 font | preload | 当前页面需要它,且发现较晚 | | 隐藏在 CSS 或 client rendering 后面的 hero image | preload | 如果 image 开始较晚,可以改善 LCP | | 用户意图之后很可能访问的下一条 route | prefetch | 在不阻塞当前页面的情况下帮助未来导航 | | 关键 third-party font/API origin | preconnect | 将连接建立从关键路径中移除 | | 可能但不确定的 third-party origin | dns-prefetch 或不使用 | 成本更低,置信度也更低 | | 首屏以下 image | none | 让 lazy loading 和浏览器优先级发挥作用 |

平静的规则

Resource hints 在无聊且具体时效果最好。一个 font。一个 LCP image。一个重要 third-party origin。一次有意图之后很可能发生的下一步导航。

当它们被当作乐观假设来使用时,效果很差:也许用户会需要这个,也许浏览器应该获取那个,也许更多 hints 意味着更快速度。

浏览器已经在积极优化。你的工作不是微观管理每一个请求。你的工作是在少数浏览器无法在正确时刻获得信息的场景中进行纠正。

常见问题

我应该 preload 所有 fonts 吗?
不应该。只 preload 页面早期可见文本所需的 font 文件。Preload 每一种字重和样式通常会浪费带宽,并可能延迟更重要的资源。
对每个内部链接使用 prefetch 安全吗?
通常不安全。它可能制造不必要的后台流量并浪费用户数据。优先采用基于意图的 prefetch,例如在 hover、focus、菜单打开,或出现可预测的下一步之后。
preconnect 和 dns-prefetch 有什么区别?
Preconnect 会为某个 origin 执行 DNS、TCP 和 TLS 建立。DNS-prefetch 只解析域名。Preconnect 更强,但成本也更高,因此应该在更有把握时使用。
Resource hints 能改善 Core Web Vitals 吗?
可以,尤其是 LCP,前提是它们修复了关键资源的发现过晚或连接建立问题。如果真正的问题是 assets 过大、服务器响应慢、render-blocking code 或缓存不佳,它们不会有帮助。
Resource hints 应该添加在 HTML 里还是 HTTP headers 里?
两者都可以。对于页面特定的 hints,HTML 更容易理解。HTTP Link headers 在服务器早于 HTML 解析之前就知道关键资源时会有用,但需要仔细测试,以避免重复或过时的 hints。

来源与进一步阅读

  1. MDN: rel=preload
  2. MDN: Resource hints
  3. web.dev: Preconnect and DNS-prefetch
  4. W3C: Resource Hints
关于作者
The Wux Webtools Team

最后更新:

继续阅读