preload、prefetch、dns-prefetch 和 preconnect 有什么区别?应该如何使用它们优化资源加载?
选择资源提示时,先判断两个问题:
| 方式 | 使用范围 | 浏览器执行的工作 | 优先级与特点 | 典型场景 |
|---|---|---|---|---|
preload | 当前页面 | 提前请求并缓存指定资源 | 根据 as 对应的资源类型确定,可用 fetchpriority 调整 | 关键字体、发现较晚的 LCP 图片、关键脚本 |
modulepreload | 当前页面 | 提前获取并处理 ES Module,放入模块映射 | 专门面向模块脚本 | 入口模块、关键依赖模块 |
prefetch | 后续导航 | 推测性地请求并缓存资源 | 通常优先级很低,浏览器可以延迟或跳过 | 下一路由的 chunk、下一页资源 |
preconnect | 当前页面或后续导航 | 提前完成 DNS、TCP 和 TLS 连接准备 | 不下载资源,成本高于 DNS 解析 | 确定马上访问的第三方 CDN、字体域名 |
dns-prefetch | 当前页面或后续导航 | 只提前解析 DNS | 成本最低,收益也最有限 | 可能访问但不确定何时访问的第三方域名 |
可以简化记忆为:
这些声明的本质是向浏览器提供额外信息。尤其是 prefetch、preconnect 和 dns-prefetch,浏览器会根据网络状况、设备资源和自身策略决定是否完整执行。
preload 的价值不是让资源立即执行,而是解决资源发现得太晚的问题。
例如字体通常要等 CSS 下载、解析并匹配到文字后才能被发现;CSS 背景图也要等样式表解析后才能被发现。把这些当前页面确定会使用的关键资源写入 <head>,浏览器就能更早开始请求。
字体请求使用 CORS 模式,因此字体的 preload 通常需要添加 crossorigin,使预加载请求与之后真正使用字体时的请求模式一致。跨域字体的服务器还必须返回正确的 Access-Control-Allow-Origin 响应头;crossorigin 本身不能绕过 CORS。
这更适合通过 CSS 背景、脚本等方式较晚发现的 LCP 图片。如果图片本来就是 HTML 中靠前的 <img>,浏览器的预加载扫描器已经能较早发现它,通常直接在图片上使用 fetchpriority="high" 更简单:
fetchpriority="high" 只是优先级提示,应该只用于少量真正关键的资源,不能替代资源压缩、合理尺寸和 CDN 缓存。
预加载的 CSS 仍然需要通过样式表链接应用,预加载的经典脚本也仍然需要通过 <script> 执行:
后续请求配置一致时,浏览器会复用已经取得的资源,而不是再次下载。
as 为什么不能省略?as 告诉浏览器资源的请求目标类型,它会影响:
Accept 请求头;| 资源 | 配置 |
|---|---|
| CSS | as="style" |
| 经典 JavaScript | as="script" |
| 图片 | as="image" |
| 字体 | as="font" |
后续由 fetch() / XHR 请求的数据 | as="fetch",并保证 CORS 配置一致 |
| ES Module | 使用 rel="modulepreload" |
type 可以进一步声明 MIME 类型。如果浏览器不支持该类型,它可以直接跳过这次预加载;media 则可以避免浏览器在媒体条件不匹配时加载资源。
ES Module 默认使用 CORS 模式,并且拥有模块映射、解析和编译等专门的处理流程。对于模块脚本,应优先使用 modulepreload,而不是普通的 preload as="script":
普通 preload 主要是提前获取并缓存响应;modulepreload 还会按照模块脚本的规则处理资源,并将其放入当前文档的模块映射中,供后续执行复用。
prefetch 表示当前页面不需要该资源,但用户接下来很可能需要。浏览器可以降低它的优先级,优先保障当前页面请求:
需要注意:
prefetch 是推测性优化,不保证一定发起请求;所以不要把当前页面马上要使用的资源交给 prefetch,否则它可能因为优先级过低而延迟关键内容。
当具体资源 URL 还不能确定,但已知资源所在域名时,可以提前处理连接阶段。
一次新的 HTTPS 请求通常需要经历:
dns-prefetch 只尝试提前完成 DNS 查询;preconnect 尝试提前完成 DNS、TCP 和 TLS,但不下载具体资源;preconnect 会消耗连接、CPU 和内存,不要为大量不确定域名建立空闲连接;crossorigin 用于让连接的凭证模式与后续请求匹配,字体 CDN 是常见场景,但它并不代表服务器已经允许跨域。浏览器复用 preload 时,不是只比较 URL。预加载请求与实际请求至少要在以下方面保持一致:
as;常见问题包括:
响应式图片还要确保 preload 最终选择的 URL 与 <img srcset> 实际选择结果一致,否则也可能下载两张图片。
preload 的核心是提前发现。浏览器会根据 as 对应的资源类型分配优先级,还可以参考 fetchpriority,并不是所有 preload 都一律最高优先级。
preload 不会直接阻塞页面渲染,但它会占用网络带宽和连接资源。预加载过多非关键资源,可能反过来延迟 CSS、LCP 图片等真正关键的请求。
crossorigin是否添加 crossorigin,取决于资源最终的请求模式。盲目添加会导致 preload 与实际请求模式不一致,甚至让原本能展示的跨域图片加载失败。即使添加了它,跨域服务器仍然需要返回正确的 CORS 响应头。
先在 Network 瀑布图中确认资源是否发现过晚。如果 <img> 已经位于 HTML 前部,重复声明 preload 的收益可能很小,优先检查 fetchpriority、图片尺寸、压缩格式和服务端响应时间。
不要同时预加载同一图片的 AVIF、WebP 和 JPEG 版本。浏览器可能支持不止一种格式,从而把多个候选资源都下载下来。应该只预加载页面最终会使用的资源。
Webpack 支持通过动态导入的魔法注释生成资源提示:
这是 Webpack 的构建能力,不是 JavaScript 语法本身。不同构建工具的生成策略可能不同,最终应检查构建产物和浏览器 Network 面板,确认提示生成时机、请求优先级以及是否发生重复下载。
preload 或 modulepreload。fetchpriority="high",并验证 LCP 是否真实改善。preconnect,不太确定时使用成本更低的 dns-prefetch。prefetch。preload 用于当前导航确定需要的资源,作用是让浏览器更早发现并请求资源,但不会自动执行或应用资源;它的优先级由 as 对应的资源类型和 fetchpriority 共同影响。prefetch 用于后续导航可能需要的资源,优先级较低,浏览器可以根据运行环境延迟或跳过。preconnect 提前建立到目标域名的连接,dns-prefetch 则只提前进行 DNS 解析。实际使用时要保证 preload 的 URL、as、CORS 和凭证模式与最终请求一致,并通过 Network 瀑布图验证是否真的减少了资源发现和连接耗时。