prefetch 不适合硬写在 html 中,因会浪费流量、不可控且 safari/ios 支持差;应采用行为触发(如 hover)、动态注入 link 标签、结合网络条件兜底的方案。

prefetch 不适合直接写死在 HTML 里用于“下一页”——它会浪费流量、不可控、且 Safari/iOS WebView 支持差。真正可用的方案是行为触发 + 动态注入 + 网络条件兜底。
为什么硬写 <link rel="prefetch" href="/next-page.html"> 很危险
浏览器一旦发起 prefetch 请求,就不会因为用户关闭标签页、切走、刷新而中止;它会在后台持续下载,哪怕页面已销毁。尤其当 as="document" 时,可能连带加载该 HTML 中引用的 JS/CSS/图片,实际消耗远超预期。移动端用户在 2G/慢网下,几秒内就可能被预取掉 1–2 MB 流量,而用户根本没点进去。
-
as="document"风险最高:预取整个 HTML,易受服务端动态 nonce/token 影响,缓存后渲染失败 -
as="script"相对安全:只预取 JS chunk,不触发额外资源拉取 - Safari 对
prefetch的支持直到 iOS 16.4 才稳定,旧版 WebView 基本忽略 - Chrome Network 面板里看不到
prefetch请求的缓存状态——它走的是 HTTP 缓存,不是内存缓存,排查困难
鼠标悬停或可视区进入时动态注入 prefetch
这是目前最平衡的实践:用户有明确意图(hover、scroll into view),才触发预取,避免盲猜。用 document.createElement('link') 动态插入,比静态 <link> 更可控。
- 悬停延迟建议设为
200ms:太短易误触,太长失去意义 - 只 prefetch 当前路由能确定的 chunk 路径,比如 Vite 构建后生成的
/assets/AboutPage.xxxx.js,别写/about这种 HTML 地址 - 记得同时 prefetch 对应的 CSS(如果分离)和关键字体(但字体更推荐
preload) - React/Vue 中放在
useEffect或onMounted里执行,避免 SSR 渲染时出错
const prefetchResource = (url) => {
const link = document.createElement('link');
link.rel = 'prefetch';
link.as = 'script'; // 显式声明 as,避免降级为 document
link.href = url;
document.head.appendChild(link);
};
card.addEventListener('mouseenter', () => {
setTimeout(() => prefetchResource('/assets/DetailPage.abcd.js'), 200);
});
用 import() 替代 prefetch 更简单可靠
如果你的“下一页”是代码分割后的模块(如 React lazy + import('./About')),直接调用这个 import 表达式,浏览器就会自动预取对应 chunk —— 它本质是 runtime prefetch,优先级略高于 rel="prefetch",且天然兼容所有现代浏览器。
- 不需要知道构建后的真实文件名,Vite/Webpack 会自动 resolve
- 不会污染
,无 DOM 操作开销 - 不依赖
as属性,也不受 Safari prefetch bug 影响 - 注意:只是预取,不会执行模块;执行仍发生在真实路由跳转时
useEffect(() => {
// 首页加载完成后,预取 About 页面模块
import('./pages/About');
}, []);
移动端必须做网络条件判断
不能假设用户永远在 Wi-Fi 下。检测到弱网时,应彻底跳过 prefetch,或改用更轻量策略(如只 dns-prefetch 或 preconnect)。
- 用
navigator.connection?.effectiveType判断网络类型,'2g'/'slow-2g'时直接 return - 监听
navigator.connection?.onchange,网络变差时 abort 正在进行的 fetch(需配合AbortController) - 服务端也可根据
User-Agent或Sec-CH-UA-Mobile头,对 iOS WebView 或低端安卓设备不注入任何 prefetch 标签
真正难处理的不是“怎么加 prefetch”,而是“它什么时候不该动”。静态标签没有上下文,动态触发才有节制能力。流量、兼容性、用户意图,三者缺一不可。










