每次改一个依赖都要等整站重新打包——今天 Import Maps 把模块缓存这件事彻底原生化了
每次改一个依赖都要等整站重新打包——这件事,在用 Import Maps 的团队里已经不存在了。
问题在哪
传统bundler模式下,模块是打在一个bundle里的。每次你更新lodash版本,整个bundle的hash都要变——因为子模块路径写死在父模块里:「import ./dependency-s8df79sd.js」。改了一个深层的lodash,父模块的hash也跟着变,整个缓存失效链一路冲到最外层index.js。用户下次访问,要重新下载整个bundle。
这不是bug,是bundler处理模块路径的方式决定的。
Import Maps怎么破局
Import Maps是HTML里一段JSON,告诉浏览器:「lodash这个包名对应哪个URL」。浏览器在运行时自己解析,不用build时打包。
<script type="importmap">
{
"imports": {
"lodash": "https://esm.sh/lodash@4.17.21/lodash.js",
"app/": "./src/"
}
}
</script>
这行代码直接写进HTML,不需要任何build工具。浏览器读到这行,以后所有import ... from lodash都自动指向esm.sh上的那个确定版本。
关键改变了什么
缓存粒度变了。 以前改lodash要重新加载整个bundle;现在只有lodash自身失效,app.js不受影响。因为import map把「模块名→URL」的映射提到了页面级别,不是在编译产物里硬编码路径。
开发模式不用起服务。 小项目、HTMX页面、WordPress插件,直接在HTML里pin CDN依赖,不用npm install,不用webpack-dev-server。Chrome 89+/Firefox 108+/Safari 16.4+全部支持,全球覆盖率~95%。
scoped mappings处理版本冲突。 两个模块需要不同版本的同一个依赖?scopes字段可以按路径覆盖:
<script type="importmap">
{
"imports": {
"react": "https://esm.sh/react@18.2.0"
},
"scopes": {
"/legacy-app/": {
"react": "https://esm.sh/react@17.0.2"
}
}
}
</script>
什么时候用、什么时候不用
Import Maps不能替代bundler——它不管tree-shaking、不压缩、不code-split。生产环境如果在意这些,bundler还是要的。
它真正的价值是:把「包名→URL解析」这件事从build时搬到运行时。这意味着:
- 原型项目可以零build跑起来
- 微前端多版本共存有了原生方案
- CDN缓存策略可以从「整包失效」优化到「单模块失效」
三步落地
- 找场景:看项目中是否有「只用了一两个npm包但要搭整套bundler」的情况,这类最值得迁移
- 生成import map:用jspm.io/generator或es-module-shims,输入package.json依赖,自动生成HTML里的import map JSON
- 验证路径:注意import map必须在第一个
<script type="module">之前出现,否则浏览器已经完成specifier解析,再晚就无效了
Import Maps不是银弹,但在「零build跑ESM」和「细粒度缓存」这两个场景上,它是浏览器原生支持的唯一方案。了解一下,说不定下次搭内部工具或者写demo的时候就用上了。
评论区
登录后可评论。