prefetch 和 preload 有什么区别?

面试结论

preloadprefetch 都是通过 <link> 向浏览器提供的资源提示,但它们表达的意图不同:

  • preload 表示“当前页面很快就会用到这个资源”,浏览器会尽早以较高优先级获取;
  • prefetch 表示“后续页面可能会用到这个资源”,浏览器通常只在当前页面空闲时以较低优先级获取。

因此,首屏关键字体、LCP 图片或被 CSS/JavaScript 较晚发现的关键资源适合 preload;下一路由的 chunk、下一篇文章的数据等非当前页面必需资源才适合 prefetch。两者只是提示,不保证一定下载;滥用 preload 还会与真正关键的资源争抢带宽。

核心对比

对比项preloadprefetch
目标优化当前页面为未来导航或后续场景准备
紧迫程度高,当前页面很快会消费低,可能以后才消费
常见优先级通常较高,但最终由浏览器调度通常很低,常在空闲时进行
是否阻塞 HTML 解析不阻塞不阻塞
是否执行资源只下载并缓存,不直接执行只下载并缓存,不直接执行
关键属性必须正确设置 as,跨域资源常需 crossorigin一般只需 href,可配合 crossorigin 等属性
典型场景字体、LCP 图片、迟发现的关键脚本或样式下一路由 chunk、下一页图片或数据
主要风险抢占带宽、重复下载、预加载后未及时使用猜测失败浪费流量,可能被浏览器忽略

“通常较高”不是协议承诺的固定优先级。浏览器会结合资源类型、页面状态、网络和省流策略自行调度。

浏览器内部发生了什么?

正常情况下,浏览器只有解析到 <img><script>、CSS 中的 @font-face,或等 JavaScript 动态创建请求后,才能发现资源。发现过晚会让关键资源错过下载窗口。

资源提示把发现时机提前到 HTML <head>

解析 HTML 中的 link
  → 识别 rel、href、as、crossorigin 等属性
  → 根据提示类型、资源类型和当前网络状态排入请求队列
  → 下载响应并放入合适的缓存
  → 页面真正请求同一资源时尝试复用

preload 解决“当前页关键资源发现太晚”的问题;prefetch 利用当前页的空闲网络时间,降低未来页面的等待。它们不会替代真正的 <script>stylesheet<img>:只有资源的实际消费者出现后,脚本才会执行、样式才会应用、图片才会渲染。

最小示例

preload:提前加载当前页关键字体和 LCP 图片

<head>
  <link
    rel="preload"
    href="/fonts/brand.woff2"
    as="font"
    type="font/woff2"
    crossorigin
  >
  <link
    rel="preload"
    href="/images/hero.avif"
    as="image"
    type="image/avif"
    fetchpriority="high"
  >
  <link rel="stylesheet" href="/styles.css">
</head>
<body>
  <img src="/images/hero.avif" alt="产品主视觉" width="1200" height="600">
</body>

字体通常到 CSS 被解析并匹配到文本后才会请求,preload 把请求提前。字体即使与页面同源,也通常按字体请求的 CORS 模式获取,所以预加载字体一般要带 crossorigin;否则实际使用时可能无法复用,造成二次下载。

fetchpriority="high" 是额外的优先级提示,不等同于 preload。前者影响请求优先级,后者主要让浏览器更早发现请求,两者可以配合,但都不应滥用。

响应式图片要保证预加载候选与 <img> 的选择一致,可以使用 imagesrcsetimagesizes,否则浏览器可能先下载一张不合适的图片:

<link
  rel="preload"
  as="image"
  href="/hero-1280.avif"
  imagesrcset="/hero-640.avif 640w, /hero-1280.avif 1280w"
  imagesizes="100vw"
>

prefetch:准备可能访问的下一路由

<link rel="prefetch" href="/assets/product-detail.js" as="script">

这段代码只是建议浏览器低优先级获取文件。当前页不会因此执行 product-detail.js;用户真正进入商品详情页、应用再 import() 同一 URL 时,才可能从缓存中复用它。

真实 SPA 通常由路由器或构建工具根据链接可见、鼠标悬停、网络状况等条件动态插入提示。上面的代码只用于演示浏览器机制,不代表框架内部实现。

as 为什么重要?

preloadas 告诉浏览器资源的目标类型,例如 scriptstyleimagefontfetch。浏览器据此设置请求优先级、Accept 请求头、内容安全策略以及缓存分区。

下面的预加载可能无法被实际字体请求正确复用:

<!-- 错误:缺少 as="font",也缺少字体常需的 crossorigin -->
<link rel="preload" href="/fonts/brand.woff2">

判断是否复用不能只看 URL。请求模式、凭证模式和资源类型也要匹配。例如预加载跨域接口数据时,提示和实际 fetch 应使用一致的 CORS/凭证语义:

<link rel="preload" href="https://api.example.com/user" as="fetch" crossorigin="anonymous">
fetch('https://api.example.com/user', { credentials: 'omit' });

即使配置正确,缓存复用仍受响应缓存头、缓存分区和浏览器实现影响,不能把资源提示当作永久缓存方案。

如何选择?

先问资源服务于哪个页面:

  1. 当前页面渲染或交互很快就需要,而且正常发现得较晚:考虑 preload
  2. 只是用户下一步很可能需要,当前页面不依赖:考虑 prefetch
  3. 只想提前完成 DNS、TCP 或 TLS 建连,不想下载具体资源:使用 dns-prefetchpreconnect
  4. JavaScript 模块及其依赖图需要预加载:优先考虑 modulepreload,而不是普通 preload as="script"
<link rel="dns-prefetch" href="//static.example.com">
<link rel="preconnect" href="https://static.example.com" crossorigin>
<link rel="modulepreload" href="/assets/app.js">

工程实践与验证

不要仅凭“感觉关键”添加提示。应先用 Chrome DevTools 的 Network、Performance 面板或 WebPageTest 验证:

  • 资源原本是否发现得晚;
  • 添加后请求开始时间是否提前;
  • Initiator、Priority 和 Size(缓存命中)是否符合预期;
  • 是否出现同一 URL 的两次请求;
  • LCP 等用户指标是否改善;
  • 弱网下是否推迟了 CSS、主脚本等更重要的资源。

生产环境通常只 preload 极少数经过测量确认的首屏资源。对于 prefetch,还应考虑用户是否开启省流模式、网络质量、命中概率和资源大小;浏览器可能基于这些条件降低优先级或完全忽略提示。

服务端也可以通过 HTTP Link 响应头发送资源提示,使浏览器在解析 HTML 前发现资源,但是否支持、何时处理以及优先级仍由客户端决定:

Link: </images/hero.avif>; rel=preload; as=image

常见陷阱

  • 把 preload 当成执行标签:它只获取资源,仍需 <script><link rel="stylesheet"><img>fetch() 消费。
  • 预加载所有首屏文件:过多高优先级请求会争抢连接和带宽,反而让真正关键资源变慢。
  • as 填错或遗漏:请求上下文不同可能导致资源不能复用,甚至被安全策略拦截。
  • 字体忘记 crossorigin:预加载与实际字体请求模式不匹配,常造成重复下载。
  • 预加载响应式图片的错误候选:预加载一张、srcset 又选择另一张,会浪费流量。
  • 把 prefetch 当成可靠缓存:它是可被忽略的提示,缓存也可能在导航前被清理或因缓存策略而无法复用。
  • 混淆 prefetchdns-prefetch:前者下载具体资源,后者只提前做 DNS 解析。
  • 忽略 URL 一致性:查询参数、重定向、凭证模式等差异都可能破坏复用。

模拟面试官深挖

1. 两者会阻塞 HTML 解析吗?

不会。它们通过 link 发起提示请求,HTML 解析可以继续;但下载会占用网络资源,间接影响其他请求。

为什么问:检查是否把“高优先级下载”和“解析阻塞”混为一谈。回答重点:CPU 解析阻塞与网络竞争是两个维度。

2. preload 下载的 JavaScript 会立即执行吗?

不会。还要由 <script> 或动态导入真正消费它。preload 只负责提前获取。

为什么问:检查是否理解资源获取和资源执行的边界。回答重点:下载不等于执行。

3. 为什么 preload 必须正确设置 as

浏览器需要用它确定资源类型、请求头、优先级、安全策略和缓存上下文;填错可能无法被真实请求复用。

为什么问:从 API 定义追到请求调度和缓存匹配。回答重点as 不只是描述信息。

4. 为什么字体 preload 常要加 crossorigin

字体获取通常使用 CORS 请求模式。提示请求与 CSS 触发的真实字体请求模式不一致时,缓存条目可能不能复用。

为什么问:考察重复下载这一高频工程问题。回答重点:同 URL 不代表同请求上下文。

5. preload 与 fetchpriority="high" 有什么区别?

preload 让浏览器更早发现资源;fetchpriority 提示浏览器调整已发现资源的相对优先级。它们解决不同问题,可以配合使用。

为什么问:考察对“发现时机”和“调度优先级”的拆分。回答重点:不要把两者视为同义词。

6. prefetch 为什么不保证生效?

它只表达低紧迫度的未来需求。浏览器可根据网络、省流模式、页面状态和自身策略决定降级或忽略。

为什么问:避免把资源提示描述为强制指令。回答重点:提示的控制权在浏览器。

7. preload、prefetch、preconnect 如何选?

当前页迟发现的关键资源用 preload;未来可能使用的具体资源用 prefetch;只想提前建立到第三方源的连接用 preconnect

为什么问:考察相似概念的边界。回答重点:分别对应当前资源、未来资源和连接准备。

8. 如何证明添加 preload 后真的变快了?

对比添加前后的请求开始时间、优先级、重复请求、关键资源竞争和 LCP 等指标,并在弱网及真实用户环境验证。如果只提前了请求却没有改善用户指标,甚至挤压关键 CSS,就应删除或调整。

为什么问:考察能否把知识用于生产环境。回答重点:以请求瀑布和用户指标验证,不以标签数量判断优化效果。

常见错误回答

  • preload 是同步加载,prefetch 是异步加载。”两者都不会同步阻塞 HTML 解析;差异主要是使用时机与调度意图。
  • preload 的优先级永远最高。”实际优先级由浏览器结合资源类型和页面状态决定。
  • “只要 URL 一样就肯定不会重复下载。”请求模式、凭证、as、缓存头等不一致仍可能导致不能复用。
  • prefetch 能保证下一个页面秒开。”用户可能不导航,浏览器可能不下载,缓存也可能无法命中。
  • “性能优化就应该把所有资源 preload。”这会制造带宽竞争,是典型的负优化。

总结

核心关键词:当前页与未来页、发现时机、请求优先级、缓存复用、浏览器提示

一句话本质:preload 提前暴露当前页的关键请求,prefetch 用空闲机会为可能发生的未来请求做准备。

面试时可以按三个层次回答:

  • 1 分钟:给出当前页/未来页、优先级和典型场景的对比。
  • 3 分钟:补充只下载不执行、as、字体 CORS、滥用导致带宽竞争。
  • 10 分钟:展开浏览器发现与调度链路、缓存匹配、modulepreload/preconnect 边界,以及如何用网络瀑布和 LCP 验证。