先记住这个答案
对常规 JavaScript 文件,.mjs 明确表示 ESM,.cjs 明确表示 CommonJS;.js 主要看最近父级 package.json 的 type,module 与 commonjs 分别指定两种格式。缺少明确标记的歧义输入在支持语法检测的 Node 中还可能按 ESM 专用语法识别,Node 22.7.0 默认启用了这项检测。动态 import() 在两种格式里都合法,不会单独强制文件变成 ESM。工程上应显式写 type 或扩展名,并检查最终产物及实际启动方式,避免依赖版本相关的推断。
- .mjs 和 .cjs 比包级默认更直接
- 嵌套 package.json 会改变附近 .js 文件的作用域
- 动态 import 表达式本身不是 ESM 专属语法
扩展名与包作用域共同决定普通文件
假设仓库根目录声明 type 为 module,根下的 app.js 会按 ESM 解释;其中一个子目录可以放置自己的 package.json 并声明 commonjs,使该作用域里的 .js 使用 CommonJS。外层配置不会越过这个新的包边界继续覆盖它。
下面把同名类型概念落实到三个文件。执行 node main.js 时,入口使用静态 import,工具文件使用 module.exports;两者通过互操作协作,不需要把整个仓库强制改成同一种格式。
// file: package.json
{ "type": "module" }
// file: tools/package.json
{ "type": "commonjs" }
// file: tools/value.js
module.exports = { value: 7 };
// file: main.js
import data from './tools/value.js';
console.log(data.value);这些文件按标记分别保存后运行 node main.js,输出 7。若把工具改成 .cjs,扩展名就能表达其格式;若改成 .mjs,则不能继续把 module.exports 当作 ESM 的导出接口。
歧义输入的语法检测不能当作项目配置
现代 Node 可以检查没有明确格式标记的输入,识别静态 import、export、import.meta 等只能按 ESM 成功解释的语法。这个行为随版本演进,不能把“没写 type 永远是 CommonJS”当成跨版本规则。
同时,动态 import() 可以出现在 CommonJS 中,因此看见 import 字样就推断 ESM 也不准确。排查需要读具体语法、包边界与命令参数;显式声明能让工具更一致,也减少运行时猜测和相关提示。
区分 Node、转译器与打包器各自的决定
TypeScript 或打包工具可能把源码 import 转成 require,再输出到另一个目录。源码检查通过后,产物若落进 type 为 module 的 .js 作用域,仍可能因 require 不存在而失败,问题发生在产物与执行环境的组合上。
命令行字符串和标准输入又有 --input-type 等独立控制方式,不应把临时 node -e 的结果直接等同于磁盘文件。复现时记录完整入口、文件扩展名、最近配置与 Node 版本,才能缩小差异。
容易答错的地方
- 只改 type 不检查产物扩展名
- 包级切换会影响很多 .js 文件,包括脚本与配置。应检查哪些文件仍输出 CommonJS,必要时保留 .cjs 或独立作用域,并验证实际运行入口,避免一次配置修改造成批量运行时故障。
- 认为调用 require 就把目标变成 CommonJS
- 加载器调用方式与目标文件格式不能简单画等号,支持互操作的 Node 可以从 CommonJS 加载符合条件的 ESM。目标解析仍受格式标记影响,异步模块图等限制也不会因此消失。
面试官还会怎么问?
package.json 的 main 能指定模块格式吗?
main 主要描述入口位置,不能代替 type 或明确扩展名来声明文件格式。即使入口解析到了正确路径,内容和格式不匹配仍会失败,因此要分别验证入口解析和模块求值。
浏览器中的 type module 和包字段一样吗?
二者处在不同执行环境:浏览器通过脚本加载方式识别模块,不会遍历服务器目录读取 package.json。前端构建可能连接这些约定,但不能把 Node 的磁盘包作用域规则直接套到浏览器。
为什么编辑器不报错而 node 执行失败?
编辑器可能依据 TypeScript 配置和类型声明判断源码,而 Node 执行的是生成后的 JavaScript 及其所在目录规则。应检查构建产物和真实启动命令,类型检查通过不代表模块格式组合一定可运行。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。