Skip to content

preload / prefetch / preconnect

资源提示(Resource Hints)是一组 HTML 特性,允许开发者主动告知浏览器关于资源加载的意图。通过 preloadprefetchpreconnect 等机制,可以显著优化页面加载性能。

前置知识

阅读本节前,建议先了解:favicon 图标

基础概念

为什么需要资源提示

浏览器默认的资源加载策略是"按需加载"——当解析到需要资源的标签时才开始加载。但这意味着关键资源可能加载较晚,导致页面渲染延迟。

资源提示让开发者能提前告知浏览器:"我接下来会需要这些资源,请尽早开始准备。"

资源提示分类

类型机制优先级触发时机
preconnect建立连接当前页面
dns-prefetchDNS 解析当前页面
preload预加载资源当前页面
prefetch预取资源最低未来导航
modulepreload预加载 ES 模块当前页面
prerender预渲染页面未来导航(已废弃)

语法

dns-prefetch

提前解析域名的 DNS,不建立连接:

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

preconnect

提前建立 TCP 连接(含 DNS 解析、TCP 握手、TLS 协商):

html
<link rel="preconnect" href="https://cdn.example.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>

preload

预加载当前页面即将需要的资源:

html
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="hero-image.jpg" as="image">
<link rel="preload" href="critical.js" as="script">
<link rel="preload" href="critical.css" as="style">

prefetch

预取下一页面可能需要的资源(低优先级):

html
<link rel="prefetch" href="next-page.js">
<link rel="prefetch" href="next-page-data.json">

modulepreload

专门用于预加载 JavaScript ES 模块:

html
<link rel="modulepreload" href="app.mjs">

详细说明

dns-prefetch 详解

工作原理:浏览器在空闲时提前解析指定域名的 DNS,将结果缓存到本地 DNS 缓存中。

适用场景

  • 页面中会从第三方域名加载资源(CDN、字体、分析工具等)
  • 节省 DNS 查询时间(通常 20~120ms)
html
<!-- 为第三方域名预先解析 DNS -->
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
<link rel="dns-prefetch" href="https://fonts.gstatic.com">
<link rel="dns-prefetch" href="https://cdn.example.com">
<link rel="dns-prefetch" href="https://www.google-analytics.com">

特点

  • 消耗极低(只做 DNS 查询)
  • 适合所有第三方域名
  • 可以大量使用(每个约消耗极少的资源)

preconnect 详解

工作原理:不仅解析 DNS,还完成 TCP 握手和 TLS 协商。相当于"预热"了连接。

适用场景

  • 跨域请求的关键资源(字体、API、CDN 资源等)
  • 连接建立成本较高的场景(HTTPS、HTTP/2)
html
<!-- 为 Google Fonts 建立预连接 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

crossorigin 属性:当资源需要跨域请求时(如字体文件),必须添加 crossorigin 属性:

html
<!-- 有 crossorigin:建立完整的 TLS 连接 -->
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

<!-- 无 crossorigin:只做 DNS + TCP(不包含 TLS) -->
<link rel="preconnect" href="https://cdn.example.com">

特点

  • 比 dns-prefetch 消耗更多资源(占用连接池)
  • 适合 2~4 个关键域名
  • 连接有生命周期(约 10 秒不使用会被关闭)

preload 详解

工作原理:以高优先级下载指定资源,并将其放入浏览器缓存。下载完成后资源处于"待用"状态,直到页面中实际引用到它。

as 属性(重要):告诉浏览器资源的类型,以便正确处理:

as 值资源类型是否需要 crossorigin
scriptJavaScript同源不需要,跨域需要
styleCSS 样式表同源不需要,跨域需要
font字体文件总是需要 crossorigin
image图片不需要
fetchfetch/XHR 请求需要
audio音频不需要
video视频不需要
documentiframe 页面不需要
embed嵌入内容不需要

type 属性:进一步指定资源的 MIME 类型,帮助浏览器判断是否需要处理该资源:

html
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="data.json" as="fetch" type="application/json">

常用 preload 场景

html
<!-- 预加载关键字体 -->
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>

<!-- 预加载首屏大图 -->
<link rel="preload" href="/images/hero-banner.webp" as="image" type="image/webp">

<!-- 预加载关键 JS -->
<link rel="preload" href="/js/critical.js" as="script">

<!-- 预加载关键 CSS -->
<link rel="preload" href="/css/critical.css" as="style">

preload 不是 prefetch

preload 用于当前页面即将需要的资源(高优先级),prefetch 用于未来页面可能需要的资源(低优先级)。不要用 preload 加载下一页面的资源,那会浪费带宽。

prefetch 详解

工作原理:以最低优先级下载资源,放入 HTTP 缓存。不会影响当前页面的加载性能。

适用场景

  • 下一页面的关键资源
  • 用户可能访问的页面的资源
  • 搜索结果的预加载
html
<!-- 预取下一页面的 JS -->
<link rel="prefetch" href="/js/next-page.js">

<!-- 预取搜索结果页的资源 -->
<link rel="prefetch" href="/search-results.css">

与 preload 的对比

特性preloadprefetch
优先级最低
用途当前页面未来页面
时机立即下载空闲时下载
带宽影响可能影响首屏不影响首屏
适用资源关键资源非关键资源

modulepreload 详解

modulepreload 是专门为 ES 模块设计的预加载方式。与 preload as="script" 的区别在于:

  • 自动处理模块的依赖关系
  • 正确处理模块的 MIME 类型
  • <script type="module"> 配合使用
html
<link rel="modulepreload" href="/js/app.mjs">
<script type="module" src="/js/app.mjs"></script>

prerender(已废弃)

html
<!-- 已废弃!不再推荐使用 -->
<!-- <link rel="prerender" href="/next-page.html"> -->

prerender 会预渲染整个页面,消耗大量资源(CPU、内存、带宽)。现代浏览器已逐步移除对它的支持。

替代方案:使用 Speculation Rules API 或 Service Worker。

实战示例

完整的资源提示配置

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>资源提示配置示例</title>

  <!-- ===== DNS 预解析(最低成本,可以多加) ===== -->
  <link rel="dns-prefetch" href="https://fonts.googleapis.com">
  <link rel="dns-prefetch" href="https://fonts.gstatic.com">
  <link rel="dns-prefetch" href="https://cdn.example.com">
  <link rel="dns-prefetch" href="https://www.google-analytics.com">

  <!-- ===== 预连接(中等成本,只用于关键域名) ===== -->
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

  <!-- ===== 预加载关键资源(高优先级) ===== -->
  <link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
  <link rel="preload" href="/images/hero.webp" as="image" type="image/webp">
  <link rel="preload" href="/css/critical.css" as="style">

  <!-- ===== 预取未来页面资源(低优先级) ===== -->
  <link rel="prefetch" href="/about.html">
  <link rel="prefetch" href="/js/about.js">

  <!-- ===== 实际资源引用 ===== -->
  <link rel="stylesheet" href="/css/main.css">
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@400;700&display=swap">
</head>
<body>
  <h1>资源提示配置示例</h1>
</body>
</html>

资源加载时序演示

html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>资源加载时序</title>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 700px; margin: 40px auto; padding: 0 20px; }
    .timeline { border-left: 3px solid #ddd; margin: 20px 0; padding-left: 20px; }
    .timeline-item { position: relative; padding: 10px 0; }
    .timeline-item::before { content: ''; position: absolute; left: -26px; top: 14px; width: 10px; height: 10px; border-radius: 50%; background: #0066cc; }
    .timeline-item.low::before { background: #999; }
    .timeline-item.high::before { background: #ff4444; }
    .label { font-weight: bold; font-size: 14px; color: #666; }
    .time { font-size: 12px; color: #999; }
    button { padding: 10px 20px; cursor: pointer; background: #0066cc; color: white; border: none; border-radius: 4px; margin: 10px 5px; }
  </style>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link rel="preload" href="https://fonts.googleapis.com/css2?family=Roboto:wght@400&display=swap" as="style">
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@400&display=swap">
</head>
<body>
  <h1>资源加载时序</h1>
  <p>使用 Performance API 观察资源加载顺序:</p>
  <button id="btn-analyze">分析资源加载</button>
  <div id="output" class="timeline"></div>

  <script>
    document.getElementById('btn-analyze').addEventListener('click', function() {
      var output = document.getElementById('output');
      output.innerHTML = '';

      var resources = performance.getEntriesByType('resource');
      resources.sort(function(a, b) { return a.startTime - b.startTime; });

      resources.forEach(function(entry) {
        var item = document.createElement('div');
        item.className = 'timeline-item';

        // 判断资源类型
        var type = 'other';
        if (entry.name.includes('googleapis')) type = 'preconnect';
        if (entry.name.includes('gstatic')) type = 'preconnect';

        var duration = Math.round(entry.duration);
        var startTime = Math.round(entry.startTime);

        item.innerHTML =
          '<div class="label">' + entry.name.split('/').pop() + '</div>' +
          '<div class="time">开始: ' + startTime + 'ms | 耗时: ' + duration + 'ms | 类型: ' + type + '</div>';
        output.appendChild(item);
      });
    });
  </script>
</body>
</html>

注意事项

1. 不要过度使用 preconnect

每个 preconnect 会占用一个连接池中的连接(Chrome 限制同域名最多 6 个连接)。

2. preload 需要与实际使用匹配

html
<!-- 预加载字体 -->
<link rel="preload" href="/fonts/main.woff2" as="font" crossorigin>

<!-- 必须在 CSS 中使用相同的 URL -->
<style>
  @font-face {
    font-family: 'MainFont';
    src: url('/fonts/main.woff2') format('woff2');
    /* URL 必须与 preload 的 href 完全匹配 */
  }
</style>

3. 字体预加载必须加 crossorigin

html
<!-- 正确 -->
<link rel="preload" href="/font.woff2" as="font" type="font/woff2" crossorigin>

<!-- 错误!字体跨域请求需要 crossorigin -->
<!-- <link rel="preload" href="/font.woff2" as="font"> -->

4. preload 的 as 属性不能省略

html
<!-- 正确 -->
<link rel="preload" href="script.js" as="script">

<!-- 错误!没有 as 属性,浏览器不知道如何处理 -->
<!-- <link rel="preload" href="script.js"> -->

最佳实践

1. 第三方域名使用 dns-prefetch 或 preconnect

html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="dns-prefetch" href="https://cdn.example.com">

2. 首屏关键字体使用 preload

html
<link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>

3. 首屏大图使用 preload

html
<link rel="preload" href="/images/hero.webp" as="image">

4. 下一页面使用 prefetch

html
<link rel="prefetch" href="/next-page.js">

5. 避免预加载过多资源

html
<!-- 不推荐:预加载太多资源,反而影响首屏性能 -->
<link rel="preload" href="/js/a.js" as="script">
<link rel="preload" href="/js/b.js" as="script">
<link rel="preload" href="/js/c.js" as="script">
<!-- 使用构建工具自动生成 preload 链接 -->

资源提示速查表

提示语法时机优先级典型场景
dns-prefetch<link rel="dns-prefetch" href="...">当前页最低第三方域名
preconnect<link rel="preconnect" href="..." crossorigin>当前页字体 CDN、API 服务器
preload<link rel="preload" href="..." as="...">当前页最高关键字体、首屏大图
prefetch<link rel="prefetch" href="...">未来导航最低下一页面的资源
modulepreload<link rel="modulepreload" href="...">当前页ES 模块

下一节

继续学习:script 标签与 JavaScript

参考链接