Node.js 的 cjs(CommonJS)和 esm(ECMAScript Module)是两种不同的模块规范,它们的路径解析规则存在核心差异,本质是为适配不同时期的模块化需求而设计,以下从核心规则、关键区别两方面简要解析:
1. 核心解析规则
(1)CommonJS(cjs)路径解析
cjs 是 Node.js 早期默认的模块规范,主要通过 require() 加载模块,解析逻辑围绕“文件查找优先级”和“便捷性”设计:
- 优先识别文件/目录:
若传入路径是具体文件名(如require('./utils.js')),直接查找该文件;若传入目录(如require('./utils')),会依次查找目录下的index.js(默认入口)、package.json中main字段指定的文件。 - 无扩展名时的“猜测” :
若未指定文件扩展名(如require('./utils')),Node.js 会自动尝试补全.js、.json、.node(C++ 扩展模块)三种扩展名,按顺序查找存在的文件。 - 依赖查找范围:
加载非相对路径模块(如require('lodash'))时,会从当前文件所在目录的node_modules开始向上遍历,直到找到对应模块,或到达系统根目录。
(2)ECMAScript Module(esm)路径解析
esm 是 ES6 标准模块规范,Node.js 从 v14.3.0 起稳定支持,通过 import/export 加载模块,解析逻辑更严格,更贴近浏览器端规范:
- 必须指定完整路径/扩展名:
不支持“自动补全扩展名”,若加载相对路径模块,必须写全文件名(如import './utils.js',不能写./utils);若加载目录,需通过package.json的exports字段明确指定入口(如./utils需在exports中映射到具体文件),不默认查找index.js。 - 依赖查找的“严格映射” :
加载非相对路径模块(如import 'lodash')时,同样从node_modules查找,但优先读取模块package.json的exports字段(若存在),按字段定义的映射规则加载,而非直接找main字段(cjs 优先),避免路径歧义。
2. 关键区别总结
| 维度 | CommonJS(cjs) | ECMAScript Module(esm) |
|---|---|---|
| 路径扩展名 | 可省略(自动补全 .js/.json/.node) | 必须写全(如 ./utils.js,不可省略) |
| 目录入口查找 | 默认找目录下 index.js 或 package.json main | 需通过 package.json exports 字段指定入口 |
| 依赖解析优先级 | 优先用 package.json main 字段 | 优先用 package.json exports 字段 |
| 灵活性 vs 严格性 | 更灵活(允许省略扩展名、默认 index) | 更严格(避免路径歧义,适配浏览器规范) |
这种差异的核心原因是:
- cjs 是 Node.js 原生设计的“服务器端模块规范”,侧重开发便捷性;
- esm 是 ES 标准规范,需兼顾浏览器端和 Node.js 跨环境一致性,因此设计更严格,减少解析不确定性。