大型单页应用首屏Bundle必须用rel="preload"而非script标签引入,因其在HTML解析初期并行下载,避免延迟400–800ms;模块项目应改用rel="modulepreload"以递归预取静态import依赖,且需确保as属性、MIME类型、路径一致性及禁用hash。

大型单页应用(SPA)的首屏 Bundle 必须用 rel="preload",不能用 <script src="main.js"> 直接引入——否则下载起始时间晚 400–800ms,且无法抢占网络空闲窗口。
为什么 main.js 必须 preload 而不是 script 标签引入
浏览器解析 HTML 时,遇到 <script type="module"> 或执行到 import('./app.js') 才发起请求,此时 HTML 解析已过半,带宽可能被 CSS、字体等占用。而 <link rel="preload" href="main.js" as="script"> 在 <head> 解析阶段就触发下载,与 HTML 解析并行。
- 必须放在
<head>最早位置,早于所有<script>标签 - 漏写
as="script"会导致 Priority 显示为Low,等同于普通 fetch,后续<script src="main.js">仍会重复请求 - 服务端返回的 MIME 类型必须是
application/javascript,否则 Safari/Edge 可能拒绝执行
modulepreload vs preload:ES 模块项目别混用
如果你的 SPA 主入口是 type="module",且内部有 import { render } from './core.js' 这类静态 import,仅用 rel="preload" 是不够的:它只下 main.js,子模块仍要等解析后才请求,多轮 RTT 延迟明显。
- 改用
<link rel="modulepreload" href="main.js">,它会预解析main.js并递归预取所有静态 import 的依赖 -
modulepreload不允许加as属性,加了会被忽略 -
href必须和import()或type="module"中写的路径完全一致(包括查询参数,如?v=1.2) - 多个
modulepreload标签之间无顺序依赖,但必须全部出现在首个模块脚本之前
构建产物带 hash 会让 preload 失效
Webpack/Vite 默认输出带 hash 的文件名(如 app.a1b2c3.js),每次构建路径都变,导致 rel="preload" 缓存失效、重复下载,甚至指向 404。
立即学习“前端免费学习笔记(深入)”;
- 对首屏关键 Bundle,建议禁用 hash 或固定文件名(如
main.js) - Vite 可通过
build.rollupOptions.output.entryFileNames控制输出名;Webpack 配output.filename+HtmlWebpackPlugin动态注入真实路径 - 绝对不要在 HTML 中硬编码带 hash 的路径,否则上线后 preload 标签直接失效
- 若用
integrity,preload 标签也必须带完全相同的值,否则缓存不复用
preload 不是跨页面预加载方案
rel="preload" 只对当前页有效,页面跳转后预加载即终止,缓存也不跨会话复用。想预加载下一页资源,必须用 rel="prefetch"。
- 滥用
preload做跨页预载,会抢占首屏带宽,拖慢当前页渲染 -
prefetch请求在 Network 面板中Initiator显示为prefetch,Priority应为Low;若看到Highest,说明误用了preload - prefetch 路径必须为绝对或根相对(如
/js/next-page.js),避免子路径下解析失败
最容易被忽略的是:preload 的 as 属性、MIME 类型、路径一致性、以及模块场景下该用 modulepreload 而非 preload —— 少一个,就等于没写。



















