rel="prefetch"不能预加载后续页面,仅在onload完成、主线程空闲、网络有余量时低优先级缓存静态资源,不执行JS、不解析HTML、不恢复状态;需同源、静态写入head、正确as属性且服务端配Cache-Control才可能生效。

rel="prefetch" 标签确实能“在后台默默加载下一页”,但这个“默默”非常脆弱——它不执行 JS、不解析 HTML、不恢复状态,且只在三个硬条件同时满足时才可能触发:页面已 window.onload、主线程空闲、网络有低优先级余量。多数场景下,它根本没发出请求。
为什么写了 <link rel="prefetch"> 却看不到 Network 请求
这不是代码错了,是浏览器压根没启动它:
- Chrome DevTools 默认隐藏
prefetch请求,必须手动勾选 Network 面板左上角的 “Prefetch” 类型过滤器 - 页面还没完成
onload(比如表单校验卡住、第三方 SDK 拖慢主线程),prefetch 就不会发起 -
href是跨域 URL(协议/域名/端口任一不同),浏览器静默忽略,连 DNS 查询都不会发 -
as属性缺失或写错(如写成as="js"或as="fetch"),整个标签被降级或丢弃 - 服务端响应头没带
Cache-Control: public, max-age=31536000,资源进不了 HTTP 缓存,prefetch 白做
哪些写法基本等于无效
以下常见操作在真实项目中失败率极高:
- 写
<link rel="prefetch" href="/next-page.html" as="document">:Safari iOS 16.4 之前基本忽略;Chrome 虽支持,但会顺带拉取该 HTML 里所有<script>和图片,流量常达 1–2 MB;若服务端返回含动态nonce或 CSRF token 的 HTML,缓存后跳转直接白屏 - 用相对路径如
./step2.js或step2.html:嵌套路由下解析失败,href变成 404 - 动态插入:
document.createElement('link')+appendChild():90% 场景下不触发请求,浏览器只认初始 HTML 解析阶段就存在的<link> - 对字体、图标、带 query 参数的 URL(如
/detail?id=123)使用 prefetch:字体因不触发 CORS 预检而静默失败;query 参数导致 URL 不被缓存识别
真正能落地的写法:只预取构建产物中的固定 chunk
目标必须是打包工具(Vite/Webpack)输出的、路径稳定不变的资源,比如:
立即学习“前端免费学习笔记(深入)”;
<link rel="prefetch" href="/assets/DetailPage.abcd123.js" as="script"> <link rel="prefetch" href="/assets/DetailPage.efgh456.css" as="style">
-
href必须是绝对路径或根相对路径(以/开头),不能含 query 参数 -
as值必须准确:script对 JS,style对 CSS,document仅限极少数可控静态页(仍建议避开) - 服务端响应头必须含
Cache-Control: public, max-age=31536000,否则下次跳转仍走网络 - 这两行必须写在
<head>中<meta charset>和<title>之后,且初始 HTML 就存在
比静态 prefetch 更可靠的做法:用 import() 触发
如果你的“下一页”已做路由懒加载(如 React 的 lazy(() => import('./DetailPage'))),直接调用该 import() 表达式即可:
- 浏览器自动预取对应 chunk,优先级略高于
prefetch,全平台兼容 - 可精准控制时机:用户鼠标悬停卡片时延迟 200ms 后调用,避免误触
- 无需操心
as、缓存头、路径解析,失败时还能 catch 错误 - 它不依赖
onload,也不受主线程阻塞影响,行为完全由 JS 运行时掌控
复杂点在于:无论你用哪种方式,它都只缓存文件,不解决组件挂载、接口重拉、表单状态恢复这些真问题——那些还得靠业务逻辑自己处理。



















