Node.js通过文件后缀.mjs或package.json中"type":"module"判断ES模块,否则为CommonJS;二者不可混用,require与import不能交叉使用,且export default与module.exports在绑定机制、循环依赖处理上存在本质差异。

CommonJS 和 ES6 模块(import/export)不是同一套机制,不能混用;Node.js 从 v12 起默认支持 ES 模块,但需明确标识,否则按 CommonJS 解析。
如何判断当前文件走的是 CommonJS 还是 ES 模块
Node.js 不看文件扩展名,而看两个条件是否同时满足:
- 文件后缀为
.mjs,或 -
package.json中设置了"type": "module"
只要满足其一,该文件就以 ES 模块方式加载;否则一律按 CommonJS 处理。即使写了 import,在 CommonJS 环境下也会报错:SyntaxError: Cannot use import statement outside a module。
require() 和 import 不能交叉使用
这是最常踩的坑:你不能在 ES 模块里写 require(),也不能在 CommonJS 里写 import。但有例外:
立即学习“Java免费学习笔记(深入)”;
- ES 模块可通过
import()(动态导入)加载 CommonJS 模块(返回一个 Promise) - CommonJS 可通过
require('module').createRequire()创建一个require函数来加载 ES 模块(仅限 Node.js v12.20+)
不过这些属于过渡方案,不建议在新项目中依赖。
Modoer 是一款以本地分享,多功能的点评网站管理系统。采用 PHP+MYSQL 开发设计,开放全部源代码。因具有非凡的访问速度和卓越的负载能力而深受国内外朋友的喜爱,不局限于商铺类点评,真正实现了多类型的点评,可以让您的网站点评任何事与物,同时增加产品模块,也更好的网站产品在网站上展示。Modoer点评系统 2.5 Build 20110710更新列表1.同步 旗舰版系统框架2.增加 限制图片
exports、module.exports 和 export default 的行为差异
CommonJS 是运行时赋值,ES 模块是编译时绑定 —— 这导致关键区别:
/* commonjs.js */
let count = 0;
module.exports = { getCount: () => count++ };
/ main.cjs /
const { getCount } = require('./commonjs.js');
console.log(getCount()); // 0
console.log(getCount()); // 1
/* esm.mjs */
let count = 0;
export default { getCount: () => count++ };
/ main.mjs /
import obj from './esm.mjs';
console.log(obj.getCount()); // 0
console.log(obj.getCount()); // 1
看起来一样?但注意:如果模块内部改写 export default 对应的变量(比如重新赋值整个对象),ES 模块不会响应,因为导出的是绑定,不是引用。而 CommonJS 导出的是值的拷贝(浅拷贝)或引用(取决于你赋什么),行为更“直观”但也更难预测。
循环依赖时的行为完全不同
CommonJS 遇到循环 require,会返回当前已执行部分的 exports 对象(可能为空或不完整);ES 模块则在解析阶段就建立绑定,即使模块还没执行完,导入的变量也存在,只是值可能是 undefined(对于具名导出)或 {}(对于 export default)。
这意味着:在 ES 模块中,循环依赖更容易触发 Cannot access 'xxx' before initialization 类型错误;而 CommonJS 更容易静默失败或拿到半成品对象。
真正麻烦的是跨模块类型混用 —— 比如一个 .cjs 文件 require 一个 .mjs 文件,或反过来。Node.js 会强制转换,但转换逻辑复杂,且不同版本行为不一致。别赌运气,统一模块系统。








