每次改一个依赖都要等整站重新打包——今天 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缓存策略可以从「整包失效」优化到「单模块失效」

三步落地

  1. 找场景:看项目中是否有「只用了一两个npm包但要搭整套bundler」的情况,这类最值得迁移
  2. 生成import map:用jspm.io/generator或es-module-shims,输入package.json依赖,自动生成HTML里的import map JSON
  3. 验证路径:注意import map必须在第一个<script type="module">之前出现,否则浏览器已经完成specifier解析,再晚就无效了

Import Maps不是银弹,但在「零build跑ESM」和「细粒度缓存」这两个场景上,它是浏览器原生支持的唯一方案。了解一下,说不定下次搭内部工具或者写demo的时候就用上了。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 10 阅读