modulepreload 是 HTML 中用于预加载 ES 模块的资源提示机制,仅发起网络请求而不执行脚本;与 defer(控制执行时机)、async(异步下载并立即执行)不同,它纯属下载调度,不干涉执行逻辑。

modulepreload 是什么,和 defer、async 有啥区别
modulepreload 是 HTML 中专门用于预加载 ES 模块(<script type="module">)的资源提示机制。它不执行脚本,只提前发起网络请求,把模块及其依赖(比如被 import 的其他 .js 文件)拉到浏览器缓存里。
和 defer 不同:defer 控制执行时机(DOM 解析完再执行),但不保证提前下载;async 会异步下载并立即执行,破坏执行顺序;而 modulepreload 纯属“下载调度”,完全不干涉执行逻辑。
常见错误现象:写了 <link rel="modulepreload"> 却没生效,或者控制台报 Failed to load module script —— 很可能路径写错,或没配 crossorigin(尤其用 CDN 或跨域模块时)。
- 必须指定
href,且值要和后续<script type="module" src="...">的src完全一致(包括查询参数) - 如果模块服务启用了 CORS(比如静态资源放在 cdn.example.com),
<link>必须加crossorigin属性,否则预加载会被浏览器忽略 - 不能用
modulepreload加载非模块脚本(比如type="text/javascript"),会静默失败
怎么写一个有效的 modulepreload link 标签
最简可用写法就是一行 <link rel="modulepreload" href="/path/to/app.js">,但实际项目中得考虑路径、CORS、依赖链这些事。
立即学习“前端免费学习笔记(深入)”;
使用场景:你有关键模块(比如路由入口 main.mjs)需要首屏就用,又不想等 <script> 解析到才开始下载。
- 路径必须是绝对路径或根相对路径(如
/assets/main.mjs),不能是相对路径(./main.mjs)—— 浏览器不支持 - 如果模块本身 import 了其他模块(比如
import { foo } from './utils.mjs'),modulepreload不会自动递归预加载它们;得手动列出来,或用构建工具生成完整依赖列表 - 多个模块预加载,建议按执行依赖顺序排列
<link>标签,虽然规范没强制,但部分浏览器会按顺序发起请求,利于 TCP 连接复用
示例:
<link rel="modulepreload" href="/assets/vendor.mjs" crossorigin> <link rel="modulepreload" href="/assets/main.mjs" crossorigin>
构建工具里怎么自动生成 modulepreload
手写 <link> 很容易漏、错、过期。真实项目基本靠构建工具注入,比如 Vite、Rollup、Webpack 插件。
性能影响:预加载太多非首屏模块,会挤占带宽,反而拖慢关键资源;但只预加载 1–3 个核心入口,通常能减少 100–300ms 的首屏 JS 加载延迟。
- Vite 默认开启
build.rollupOptions.output.manualChunks时,会自动生成modulepreload标签(在index.html中) - Rollup 用户可用
rollup-plugin-module-preload,注意它依赖rollup-plugin-html或类似插件把标签写入 HTML - Webpack 需配合
html-webpack-plugin+webpack-module-preload-plugin,配置稍重,且要注意 Webpack 5+ 的模块图解析是否包含动态import()
容易踩的坑:开发环境热更新后,HTML 里的 modulepreload href 还是旧 hash,导致 404 —— 构建插件必须监听输出文件变更,并更新 HTML 中的链接。
modulepreload 和 HTTP/2 Push、preload 的关系
modulepreload 是客户端声明式预加载,不依赖服务器配置;而 HTTP/2 Server Push 已被主流浏览器弃用(Chrome 96+ 移除),别再折腾。
和通用的 <link rel="preload"> 比:后者可预加载任何资源(字体、图片、JSON),但对模块来说,preload 缺少模块语义 —— 浏览器不会解析它的 import 依赖,也不会等它完成再执行其他模块;modulepreload 则明确告诉浏览器“这是 ES 模块,请按模块图处理”。
- 不要混用
rel="preload"和rel="modulepreload"加载同一个模块,可能触发重复请求 - 服务端渲染(SSR)场景下,若 HTML 是服务端吐出的,
modulepreload的 href 必须和服务端构建产物 hash 严格匹配,否则白屏 - 兼容性:Chrome 83+、Firefox 93+、Safari 16.4+ 支持;Edge 同 Chrome。IE 完全不支持,但本来就不支持模块语法
真正复杂的地方在于依赖拓扑 —— 你预加载了 A,A 又 import B 和 C,而 B 里又动态 import D……这种链式依赖,仅靠静态分析很难全覆盖,得结合运行时采集或构建时深度遍历。



















