prefetch 不是带宽调度机制,仅在浏览器空闲时以 low 优先级被动发起请求,无并发控制、优先级抢占或进度监听能力;大型路由预取需动态注入+条件判断,静态批量声明易造成流量浪费与缓存污染。

prefetch 在大型路由预取中根本不是带宽调度机制
它不参与带宽分配、不支持优先级抢占、不提供并发控制——浏览器只在空闲时才发请求,且默认降为 Low 优先级。所谓“调度”,其实是误读:prefetch 没有调度权,只有被动等待权。
常见错误是把多个 <link rel="prefetch"> 当成“并行预取队列”,结果发现:Chrome 可能只发出 1–2 个请求,其余被延迟甚至丢弃;Safari 可能一个都不发;Firefox 默认关闭该功能(network.prefetch-next = false)。
- 浏览器空闲 ≠ 网络空闲:即使页面已
onload,若后台有fetch()、WebSocket或长任务,prefetch 仍被挂起 - 同域名下 prefetch 并发数极低(Chrome 通常 ≤2),且无排队机制,超量声明等于白写
- 没有 API 或事件可监听 prefetch 进度,无法做 fallback 或降级(比如失败后改用
import())
大型 SPA 路由预取必须配合动态注入与条件判断
硬编码几十个 prefetch 到 是危险操作:用户只访问首页,却强制下载所有路由 JS/CSS,移动端流量和缓存污染立刻飙升。
真正可控的做法是结合路由行为,在用户交互信号出现时动态创建:
文章转信息图。将文章/笔记转化为手机可读的 HTML 信息图,自动匹配视觉风格。触发场景:文章转图、笔记转图、信息图、转小红书图、做张图、可视化这篇文章、文生图。
- 鼠标悬停菜单项时,
document.createElement('link')注入对应 chunk 的prefetch - 用户在表单第一步输入完成(如邮箱校验通过),再预取第二步的
/step2.chunk.js - 使用
IntersectionObserver监听导航区域进入视口,触发预取,避免过早加载
注意:as 值必须准确(as="script" 不可省略),且 href 必须为绝对或根相对路径(如 /js/routes/profile.abc.js),./js/... 在子路由下会解析失败。
prefetch 请求不发起的五个真实原因
写了却看不到 Network 面板里的请求,不是代码无效,而是以下任一条件未满足:
-
Chrome DevTools默认过滤prefetch类型请求 —— 必须手动勾选 “prefetch” 过滤器或清空所有筛选 - 目标资源响应头缺失
Cache-Control: public, max-age=31536000,浏览器认为“没必要缓存”,直接跳过 - 页面尚未触发
onload(例如某个<img>404 卡住 load 事件,或主线程被长任务阻塞) -
href是协议相对路径(如//cdn.example.com/a.js),被浏览器判定为非法 URL,静默忽略 - 跨域请求(协议/域名/端口任一不同)—— prefetch 不触发 DNS 查询,Network 面板里连
pending状态都不会出现
比 prefetch 更适合大型路由的替代方案
当路由数量多、路径动态、用户路径不确定时,prefetch 的“静态推测”模型迅速失效。更务实的选择是:
- 对高频路由(如用户中心、订单页)用
import()+Promise.allSettled()做懒加载预取,JS 执行可控、可监控、可降级 - 服务端渲染(SSR)场景下,用
data-prefetch属性标记可预取路由,由 hydration 后的 JS 决定是否触发fetch()或import() - 关键路由(如支付页)改用
<link rel="preload" as="script">+type="module",强制高优加载,但仅限当前导航生命周期内有效 - 完全放弃客户端预取,改由 CDN 边缘计算(如 Cloudflare Workers)根据 UA/Referer/地理位置,动态注入最可能命中路由的
prefetch标签
真正容易被忽略的一点:prefetch 只存文件到 HTTP 缓存,不执行 JS、不初始化状态、不恢复 history.state —— 它解决不了路由切换后的首屏渲染延迟,只解决“磁盘读取”这一环。而现代 SPA 的瓶颈,早就不在这里了。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










