你以为 npm 包只能在 Node.js 里用?今天 Import Maps 把这件事彻底变了

写完这句 import React from "react",你在浏览器里直接打开——它会报一个错:import raw specifier must be a URL。因为裸的 specifier(就是这种不带路径的模块名)在浏览器眼里什么都不是,这是 Node.js 和构建工具做的事情,和浏览器没关系。

这件事今天被 Import Maps 彻底变了。

一个 JSON 块,把裸 specifier 变成真实 URL

Import Maps 就是 HTML 里的一个 <script type="importmap">,里面是一段 JSON,告诉浏览器:「看到 react 这个名字,就去加载这个 URL」。

<script type="importmap">
{
  "imports": {
    "react": "https://esm.sh/react@18.3.1",
    "react-dom/client": "https://esm.sh/react-dom@18.3.1/client"
  }
}
</script>
<script type="module">
  import { useState } from "react";
  import { createRoot } from "react-dom/client";
  // 正常写你的代码
</script>

版本号直接钉死在 URL 里。升级就是改一个字符串,不用动任何一行业务代码。

配 esm.sh,npm 包直接在浏览器里跑

Import Maps 的最佳拍档是 esm.sh——这个 CDN 会自动把 npm 包转成浏览器能直接加载的 ES Module,不需要你做任何转换:

  • https://esm.sh/react@19.2.4 就是 React 19
  • https://esm.sh/preact@10.22.1 就是 Preact
  • https://esm.sh/date-fns@3.6.0/format 就是 date-fns 的某个子路径

配好 import map 之后,你的项目里可以完全不需要 Node.js、不需要 npm install、不需要 Vite、不需要 webpack。浏览器自己会解析、会加载、会缓存。

三步解析规则,最长匹配优先

Import Maps 的 specifier 解析走三步:

  1. 如果是相对或绝对 URL(./x.jshttps://...),直接走 URL 解析,不查 import map
  2. 如果有 scope 匹配(按加载模块的路径),用 scope 里的映射,路径最长优先
  3. 都不匹配,走顶层 imports,也是最长 key 优先

这意味着:

{
  "imports": {
    "lodash": "https://esm.sh/lodash-es@4.17.21",
    "lodash/": "https://esm.sh/lodash-es@4.17.21/"
  }
}

import _ from "lodash" 加载完整文件;import get from "lodash/get" 自动拼接成 https://esm.sh/lodash-es@4.17.21/get,一行不用改。

同一个包,两个版本,不同路径各用各的

scopes 字段解决了包版本的世纪难题:

{
  "imports": {
    "lodash": "https://esm.sh/lodash-es@4.17.21"
  },
  "scopes": {
    "/legacy/": {
      "lodash": "https://esm.sh/lodash@3.10.1"
    }
  }
}

/legacy/ 路径加载的模块用 lodash 3,其他地方全部用 lodash 4。浏览器自己会分流,不用任何构建配置,不需要两个 node_modules。

浏览器自己会查完这张图再加载

Import Map 加载后,在第一个模块被解析之前就固定了。解析之后这张图就是只读的,改了也没用——这是 spec 的设计,不是 bug,设计意图是防止运行时意外修改导致不确定行为。

@esm.sh/import-map 这个包(2026 年 2 月发布,npmx 收录)把 import map 做成了可编程的 API,支持从 JSON/HTML 解析、自动从 esm.sh 抓依赖生成 scope、自动补 SRI 完整性校验:

import { ImportMap } from "@esm.sh/import-map";
const im = new ImportMap();
await im.addImport("react-dom@19/client");
const raw = im.raw;
// 自动生成完整 scope 和 integrity 字段

jspm.io/generator 可以直接贴 package.json 的依赖列表,生成完整 import map 给你复制进 HTML,不用手写。

适合的场景 vs 不适合的场景

适合:

  • 内部工具、仪表盘,不想配置一整套构建链
  • 纯前端的 POC,原型阶段快速验证
  • 小工具类页面,依赖不多,不需要 tree-shaking

不适合:

Import Maps 本身不做打包、不压缩、不 tree-shaking。生产级应用如果在乎 bundle 体积,Vite/Webpack 的构建优化它替代不了。

什么时候该用?

三个问题帮你判断:

  • 依赖是 pure ESM 包(大多数新包都是)吗? → 可以用
  • 你的页面需要支持的最低浏览器是什么? → 现代浏览器(Chrome 89+/Safari 15.4+/Firefox 108+)直接原生支持
  • 你需要 tree-shaking 吗? → 需要就走构建工具,不需要就走 Import Maps

大多数内部工具和 Dashboard 三个问题答案都是「No」,这时候 Import Maps 是最干净的选择。

评论区

0 条评论

登录后可评论。

阿柯·前端架构 15 阅读