<link rel="modulepreload">是声明式预加载机制,用于提前获取ES模块及其依赖但不执行,以减少加载延迟;它在HTML解析早期触发网络请求,支持自动解析静态import依赖链,需指定有效URL、推荐加type="module"并置于head中。

在 JavaScript 中,<link rel="modulepreload"> 是一种声明式预加载机制,用于提前获取(fetch)但不执行 ES 模块及其依赖,为后续 import 做准备,从而减少模块加载延迟、提升首屏或关键交互的性能。
为什么需要 modulepreload?
浏览器默认的 import 是“按需触发”:遇到 import 语句才开始网络请求,存在请求排队、解析阻塞等问题。而 modulepreload 允许你在 HTML 解析早期就发起模块资源请求(甚至在 JS 执行前),让网络下载与 HTML 解析、JS 编译并行,尤其对深层嵌套的模块树效果明显。
它不同于 rel="preload":后者需手动指定 as="script",且不理解模块图谱;modulepreload 会自动解析模块的 import 依赖链(如 import 'lodash' 对应的完整路径),并预加载整个依赖子图(前提是路径可静态分析)。
如何正确使用 <link rel="modulepreload">
基本写法:
立即学习“Java免费学习笔记(深入)”;
<link rel="modulepreload" href="./main.mjs">
关键要求:
-
href 必须是有效模块 URL:支持相对路径、绝对路径、完整 URL;不能是裸 specifier(如
lodash),必须指向实际文件(如./node_modules/lodash-es/index.js) -
需显式声明 type(推荐):添加
type="module"更明确,避免与旧版脚本混淆 - 放在 <head> 中尽早出现:越早写入 HTML,越早触发预加载
- 不执行模块:仅 fetch + 编译(parse + compile),不执行 top-level code,也不触发副作用
常见误区与注意事项
容易踩的坑:
-
不解决动态 import() 的预加载:modulepreload 只对静态 import 生效;动态
import('./foo.mjs')无法被提前发现,需配合rel="preload"或构建工具注入 -
不会自动处理 bare specifiers:像
import React from 'react'这类写法,浏览器无法解析真实路径;必须用构建工具(如 Vite、Webpack)转成带扩展名和路径的格式,再生成对应 preload link - 重复 preload 无害但冗余:同一模块多次声明会被去重;但过多 link 标签会增加 HTML 体积和解析开销,建议只预加载关键路径入口
- 不兼容旧浏览器:Chrome 63+、Firefox 64+、Safari 15.4+ 支持;低版本忽略该标签,不影响功能,但无优化效果
配合构建工具自动生成
手动维护 modulepreload 链接不现实。主流现代构建工具已内置支持:
- Vite:默认在 HTML 中注入所有入口 chunk 的 modulepreload(含依赖)
-
Webpack 5+:启用
experiments.futureDefaults后,配合HtmlWebpackPlugin插件可输出 modulepreload - Rollup + @rollup/plugin-html:通过插件配置可注入
例如 Vite 构建后,你可能看到:
<link rel="modulepreload" href="/src/main.mjs" type="module">
<link rel="modulepreload" href="/node_modules/.vite/deps/react_jsx-runtime.js" type="module">










