modulepreload 是一种通过 在 html 解析早期预拉取 es 模块资源的机制,它只下载不解析不执行,确保后续模块脚本可立即解析执行;它不是加速 js 执行的魔法开关,也不处理跨域、路径匹配或子模块依赖等细节,需严格满足 href 一致、crossorigin 属性、绝对/根相对路径及手动声明子模块等条件才能生效。

JS 中利用模块预加载提升加载性能,核心是用 <link rel="modulepreload"> 提前拉取关键 ES 模块资源,不执行、不解析,只为后续 <script type="module"></script> 能立刻启动解析与执行。
modulepreload 是什么,它不做什么
它不是“让 JS 执行更快”的魔法开关。它只做一件事:在 HTML 解析早期(甚至 DOM 构建前)就发起网络请求,把模块文件下载进浏览器缓存。之后当脚本标签真正出现时,浏览器跳过下载阶段,直接进入解析、构建模块图、执行流程。
它和常见加载方式的区别很关键:
- defer:等 HTML 解析完才执行,但下载时机由 parser 决定,可能滞后
-
async:下载完立刻执行,会打乱模块依赖顺序,不能用于有
import的链路 - modulepreload:纯 fetch 阶段,不 parse、不执行、不建图,只确保资源“已在本地”
怎么写一个真正生效的 modulepreload
写错就等于没写——它静默失败,控制台不报错,Network 面板也看不到请求。必须同时满足以下硬性条件:
在 Linux 上通过 Chrome 挂载/VNC 自动发布小红书长笔记。Agent 撰写标题和正文后执行 xhs_publish.sh --publish --submit,触发小红书发布。
-
href 必须完全一致:协议、域名、路径、查询参数(如
?v=2.1.0)要和后续<script type="module" src="..."></script>的src逐字匹配 -
跨域必须加 crossorigin:只要模块走 CDN 或静态托管(哪怕同域但不同端口),
<link>就得带crossorigin属性,否则被忽略 -
路径必须是绝对或根相对:不能写
./main.mjs或../utils.js,得写/assets/main.mjs或https://cdn.example.com/main.mjs -
子模块需手动声明:如果
main.mjs里import './utils.js',你得额外加一行<link rel="modulepreload" href="/assets/utils.js" crossorigin>
真实项目中别手写,交给构建工具
手写极易漏、难维护,尤其构建产物带 hash(如 /assets/main.a1b2c3.js)时,人工无法同步更新 href。推荐方案:
-
Vite:默认开启
build.modulePreload,自动分析并注入所有静态 import 的模块 -
Webpack:配
webpack-preload-plugin,支持按 chunk 分组预加载 -
Rollup:用
rollup-plugin-module-preload,基于 AST 静态提取依赖链
这些工具能识别 import 和动态 import(),生成精准、可维护的预加载链,且保证路径、crossorigin、顺序全部正确。
搭配动态 import 实现更精细控制
对于非首屏、用户触发才需要的功能模块(比如编辑器、图表库),可用 import() 动态加载,并配合 modulepreload 提前拉取:
- 在页面空闲时(如
requestIdleCallback)或用户悬停按钮时,提前插入<link rel="modulepreload"> - 真正调用时再执行
const mod = await import('./editor.js'),此时资源已就位,毫秒级完成 - 比单纯
import()更快,避免了首次点击时的网络等待










