本质区别在于加载机制和执行模型:ES6模块是静态、编译时解析的接口系统,依赖关系在语法分析阶段即确定,import必须位于顶层;CommonJS是动态、运行时执行的对象系统,require可条件调用、拼接路径,模块导出为值拷贝,而ES6导出为实时绑定。

本质区别不在写法,而在加载机制和执行模型:ES6 模块是静态、编译时解析的接口系统;CommonJS 是动态、运行时执行的对象系统。
加载时机与解析阶段不同
ES6 模块在代码被 JavaScript 引擎解析(即“编译时”,非传统编译,而是语法分析阶段)就确定全部依赖关系。import 语句必须出现在顶层作用域,不能放在 if 或函数里——因为引擎需要提前知道要加载哪些模块。
CommonJS 的 require() 是普通函数调用,发生在代码实际执行时(运行时),可以条件加载、拼接路径、甚至循环调用:
- ✅ CommonJS 支持:
if (env === 'dev') require('./debug.js') - ❌ ES6 不支持:
if (...) import { x } from './x.js'(语法错误)
导出与导入的行为逻辑不同
CommonJS 导出的是 module.exports 对象的一个快照(值拷贝):基本类型复制值,对象复制引用地址(即浅拷贝)。一旦导出完成,后续对 module.exports 的修改不影响已导入的值。
ES6 模块导出的是绑定(live binding):import 进来的变量始终指向原始模块中 export 的那个声明,内部值变化会实时反映到所有导入处。
- ✅ ES6 示例:
export let count = 0;→ 其他模块import { count }→ 原模块count++→ 所有导入处看到新值 - ❌ CommonJS 示例:
module.exports = { count: 0 };→ 导入后原模块改exports.count = 1→ 导入方仍为 0
运行环境与设计定位不同
CommonJS 从诞生起就面向 Node.js 服务端,依赖同步文件 I/O,适合本地快速读取;它的模块缓存(require.cache)也基于此设计。
ES6 模块是语言级标准,原生适配浏览器网络加载场景,天然支持异步(import())、tree-shaking、循环依赖的动态绑定处理,且被现代构建工具(Vite、Webpack、esbuild)深度优化。
- Node.js 中默认使用 CommonJS,启用 ESM 需加
"type": "module"或用.mjs后缀 - 浏览器直接支持
<script type="module">,无需构建也可运行 ESM
混用风险高,不是语法替换那么简单
两者底层机制不兼容。强行混合会导致:
- import 一个 CommonJS 模块 → 可能拿到
default包裹的对象,而非预期命名导出 - require 一个 ESM 文件 → Node.js 报错
ERR_REQUIRE_ESM - 打包工具中未正确配置互操作(如 Webpack 的
resolve.exportsFields)→ 导入为空或 undefined
跨规范调用应通过工具链桥接(如 Node.js 的 createRequire 或打包器的 interop 选项),而非手动改写 import/require。


















