Node.js ESM 解析算法(Resolution Algorithm)的职责只有两个:
- 将
import 中的 specifier 解析为最终 URL - 判断该 URL 对应的模块格式
例如:
CodeBlock Loading...
Node.js 最终需要得到:
CodeBlock Loading...
以及:
CodeBlock Loading...
等模块格式。
整个 ESM 规范本质上是在描述:
CodeBlock Loading...
一、ESM_RESOLVE 总体流程
Node.js ESM 的入口算法:
CodeBlock Loading...
可以简化为:
CodeBlock Loading...
整个算法的核心目标:
CodeBlock Loading...
而不是像 CommonJS 那样不断猜测。
二、specifier 分类
Node.js 将 specifier 分为四类。
1. URL Specifier
例如:
CodeBlock Loading...
或者:
CodeBlock Loading...
如果本身是合法 URL:
CodeBlock Loading...
成功。
那么直接返回。
2. Relative Specifier
例如:
CodeBlock Loading...
Node.js 根据当前模块位置解析。
假设:
CodeBlock Loading...
执行:
CodeBlock Loading...
得到:
CodeBlock Loading...
本质上就是:
CodeBlock Loading...
3. Package Imports
例如:
CodeBlock Loading...
或者:
CodeBlock Loading...
以:
CodeBlock Loading...
开头。
会进入:
CodeBlock Loading...
读取当前包:
CodeBlock Loading...
例如:
CodeBlock Loading...
最终得到:
CodeBlock Loading...
4. Bare Specifier
例如:
CodeBlock Loading...
CodeBlock Loading...
CodeBlock Loading...
既不是:
CodeBlock Loading...
就属于裸包名。
进入:
CodeBlock Loading...
开始查找 node_modules。
三、PACKAGE_RESOLVE
这是 Node.js 查找 npm 包的核心逻辑。
例如:
CodeBlock Loading...
当前文件:
CodeBlock Loading...
Node.js 会不断向上查找:
CodeBlock Loading...
直到找到。
等价于:
CodeBlock Loading...
四、读取 package.json
找到包以后:
CodeBlock Loading...
Node.js 开始读取配置。
优先级如下:
CodeBlock Loading...
五、exports 机制
现代 Node.js 包解析几乎完全依赖 exports。
例如:
CodeBlock Loading...
允许:
CodeBlock Loading...
对应:
CodeBlock Loading...
得到:
CodeBlock Loading...
允许:
CodeBlock Loading...
对应:
CodeBlock Loading...
得到:
CodeBlock Loading...
但是:
CodeBlock Loading...
如果 exports 中不存在:
CodeBlock Loading...
则报错:
CodeBlock Loading...
六、exports 的本质
exports 可以理解为:
CodeBlock Loading...
例如:
CodeBlock Loading...
允许:
CodeBlock Loading...
禁止:
CodeBlock Loading...
即使文件真实存在。
七、Conditional Exports
exports 不一定是字符串。
可以是对象。
例如:
CodeBlock Loading...
Node.js 会根据条件选择。
ESM:
CodeBlock Loading...
匹配:
CodeBlock Loading...
得到:
CodeBlock Loading...
CommonJS:
CodeBlock Loading...
匹配:
CodeBlock Loading...
得到:
CodeBlock Loading...
八、PACKAGESELFRESOLVE
假设:
CodeBlock Loading...
当前代码就在:
CodeBlock Loading...
内部。
那么:
CodeBlock Loading...
不会去外部 node_modules 查找。
Node.js 会发现:
CodeBlock Loading...
直接使用当前 package.json 的 exports。
这就是:
CodeBlock Loading...
九、imports 机制
imports 和 exports 很像。
区别:
CodeBlock Loading...
例如:
CodeBlock Loading...
之后:
CodeBlock Loading...
实际解析:
CodeBlock Loading...
再例如:
CodeBlock Loading...
得到:
CodeBlock Loading...
十、Pattern Match
imports 和 exports 支持通配符。
例如:
CodeBlock Loading...
执行:
CodeBlock Loading...
匹配:
CodeBlock Loading...
得到:
CodeBlock Loading...
最终:
CodeBlock Loading...
十一、PACKAGETARGETRESOLVE
这是 exports/imports 真正执行映射的地方。
支持四种类型。
String
CodeBlock Loading...
直接解析。
Object
CodeBlock Loading...
根据 conditions 选择。
Array
CodeBlock Loading...
前一个失败。
继续尝试下一个。
null
CodeBlock Loading...
明确禁止导出。
URL 定位完成后。
Node.js 需要判断:
CodeBlock Loading...
.mjs
CodeBlock Loading...
.cjs
CodeBlock Loading...
.json
CodeBlock Loading...
.wasm
CodeBlock Loading...
.node
CodeBlock Loading...
原生扩展模块。
十三、type 字段的作用
对于:
CodeBlock Loading...
文件。
Node.js 会查找最近的 package.json。
例如:
CodeBlock Loading...
那么:
CodeBlock Loading...
被解释为:
CodeBlock Loading...
如果:
CodeBlock Loading...
则:
CodeBlock Loading...
被解释为:
CodeBlock Loading...
因此:
CodeBlock Loading...
永远 ESM。
CodeBlock Loading...
永远 CommonJS。
CodeBlock Loading...
取决于 type。
十四、为什么 ESM 不支持目录导入
CommonJS:
CodeBlock Loading...
Node.js 会尝试:
CodeBlock Loading...
ESM:
CodeBlock Loading...
不会猜。
如果:
CodeBlock Loading...
是目录。
直接报错:
CodeBlock Loading...
正确写法:
CodeBlock Loading...
或者:
CodeBlock Loading...
十五、LOOKUPPACKAGESCOPE
Node.js 如何找到最近的 package.json?
算法:
CodeBlock Loading...
例如:
CodeBlock Loading...
查找:
CodeBlock Loading...
找到第一个就停止。
十六、自定义 ESM Resolver
Node.js 默认解析:
CodeBlock Loading...
如果想支持:
CodeBlock Loading...
怎么办?
答案:
CodeBlock Loading...
例如:
CodeBlock Loading...
运行:
CodeBlock Loading...
之后:
CodeBlock Loading...
就会自动映射:
CodeBlock Loading...
十七、完整解析链路
Node.js ESM 解析可以总结为:
CodeBlock Loading...
总结
Node.js ESM 的核心思想不是“寻找文件”。
而是:
CodeBlock Loading...
整个 exports、imports、type、conditional exports、loader 机制,本质上都是围绕这一目标构建的。
ESM 解析模型相比 CommonJS 更严格、更静态、更接近浏览器,也更适合现代工具链(Vite、Webpack、Rspack、Rollup、Turbopack)进行分析和优化。