结论:<link rel="prefetch"> 不是“加了就快”,而是“在用户大概率会点进去时,悄悄把资源塞进 HTTP 缓存里”;它不优化首屏、不执行脚本、不发 API 请求,误用会浪费流量、污染缓存,且 Safari 旧版基本无效。

直接说结论:用 <link rel="prefetch"> 预取资源,不是“加了就快”,而是“在用户大概率会点进去时,悄悄把资源塞进 HTTP 缓存里”。它不解决首屏加载慢,也不执行脚本、不取 API 数据,写错反而浪费流量、污染缓存、在 Safari 上基本白搭。
什么时候该用 rel="prefetch"?
只在明确知道用户下一步大概率访问某个静态资源(且路径稳定)时才用。典型场景包括:
- 单页应用(SPA)首页预取「用户中心」模块的 JS 文件,比如
/assets/user-center.8a2b.js - 电商列表页,根据历史点击率预取前 2–3 个商品详情页的 HTML(
/product/123.html)和对应 CSS - 表单多步流程中,第二步页面资源已构建完成、路径固定,且用户填完第一步后跳转概率 >70%
别用它预取 /api/user/profile 这类接口——prefetch 不发 fetch 请求,也不会解析 JSON;也别用来“优化首屏”,它根本不在 onload 前触发。
rel="prefetch" 必须带 as 属性,否则大概率失效
浏览器靠 as 判断资源类型,从而设置正确的请求头(如 Accept)、缓存策略和优先级。漏掉或写错 as,请求可能被降级为低优先级 fetch,甚至被忽略。
立即学习“前端免费学习笔记(深入)”;
- 预取 JS 模块:
<link rel="prefetch" href="/assets/detail.4f9c.js" as="script"> - 预取 HTML 页面:
<link rel="prefetch" href="/pages/checkout.html" as="document"> - 预取关键 CSS:
<link rel="prefetch" href="/css/detail.css" as="style"> -
as="fetch"是无效值,别写;as="json"浏览器不识别,也不支持
注意:如果目标 HTML 中包含动态 nonce 或服务端渲染的 token,预取后缓存的 HTML 可能因校验失败而无法渲染——这种页面根本不适合 prefetch。
静态写死 <link rel="prefetch"> 在 <head> 里很危险
硬编码到 HTML 模板里,等于强制所有用户无差别下载一个可能永远用不到的资源。尤其移动端,后果明显:
- 用户切页或关闭标签后,prefetch 请求仍后台持续下载(浏览器不中止)
- Safari 直到 iOS 16.4 才开始稳定支持,旧版 WebView 完全忽略,写了等于白加
- 预取
as="document"会连带加载该 HTML 引用的所有 JS/CSS/图片,实际流量远超预期 - Chrome DevTools Network 面板默认过滤 prefetch 请求,需手动勾选才能看到,排查困难
更可控的做法是:用 document.createElement('link') 动态注入,在用户悬停卡片(mouseenter)、元素进入视口(IntersectionObserver)或按钮聚焦后延迟 200ms 再触发。React 中放在 useEffect,Vue 中放在 onMounted,同时加 try/catch 捕获错误。
验证 prefetch 是否真正生效,不能只看 Network 请求
看到请求发出 ≠ 资源进了缓存 ≠ 后续导航真复用了。必须分三步确认:
- 在 Chrome DevTools Network 面板,打开过滤器,勾选
Prefetch类型,确认请求发出且状态码为 200 - 刷新后跳转到目标页面,观察 Network 中对应资源是否显示
from disk cache或from memory cache - 对比跳转前后该资源的加载耗时:若从几百毫秒降到几毫秒,说明 prefetch 生效;若仍是网络请求,则可能是缓存策略(如
Cache-Control: no-cache)或 MIME 类型不匹配导致未复用
最容易被忽略的一点:prefetch 的缓存键严格依赖 URL 和请求头(尤其是 Accept),如果服务端对同一 URL 返回不同内容(如基于 UA 或语言做 SSR),缓存很可能失效,但你完全察觉不到。



















