> 技术文档 > CommonJS和ES6 Modules区别_commonjs module when esm syntax is not allowed in

CommonJS和ES6 Modules区别_commonjs module when esm syntax is not allowed in


CommonJS 和 ES Modules(ESM)的区别

CommonJSES Modules (ESM) 是 JavaScript 中两种主流的模块规范,分别用于不同场景(Node.js 与浏览器/现代 Node.js),核心区别体现在语法、加载机制、运行时行为等方面。

1. 语法差异(最直观的区别)

CommonJS
  • 导出:使用 module.exportsexports
  • 导入:使用 require()
// 导出(math.js)function add(a, b) { return a + b; }module.exports = { add }; // 整体导出// 或 exports.add = add; // 单个导出// 导入(index.js)const math = require(\'./math.js\');console.log(math.add(1, 2)); // 3
ES Modules (ESM)
  • 导出:使用 export(命名导出)或 export default(默认导出)
  • 导入:使用 import
// 导出(math.js)export function add(a, b) { return a + b; } // 命名导出// 或 export default { add }; // 默认导出// 导入(index.js)import { add } from \'./math.js\'; // 对应命名导出// 或 import math from \'./math.js\'; // 对应默认导出console.log(add(1, 2)); // 3

2. 加载机制:运行时加载 vs 编译时加载

CommonJS:运行时动态加载
  • 模块加载发生在代码执行阶段(运行时),require() 是一个函数,执行时才会加载模块。
  • 支持动态导入(可在条件语句中加载模块):
  • CommonJs是动态语法可以写在判断里
// 合法:根据条件动态加载if (condition) { const math = require(\'./math.js\');}
  • 加载的是模块的拷贝(值传递):导入后,模块内部的后续修改不会影响导入结果。
ESM:编译时静态加载
  • 模块加载发生在代码解析阶段(编译时),import 声明必须放在文件顶部,且不能在条件语句中使用(静态分析)。
  • ES6 Module静态语法只能写在顶层,不支持直接在条件语句中写 import,但可通过 import() 函数实现动态加载(返回 Promise):
// 合法:动态导入(ES2020 新增)if (condition) { import(\'./math.js\').then(({ add }) => console.log(add(1, 2)));}
  • 加载的是模块的引用(引用传递):导入后,模块内部的修改会实时反映到导入处。

3. 模块依赖:动态依赖 vs 静态依赖

  • CommonJS:依赖关系在运行时确定,模块路径可动态生成(如拼接字符串):

CommonJS 是运行时解析
CommonJS 的模块加载是动态的,require()是一个运行时执行的函数,可以写在代码的任何位置(如条件语句、循环中)。模块的依赖关系只有在代码执行到require()语句时才会被解析和加载,无法在编译阶段(代码执行前)确定完整的依赖树。

const path = \'./math.js\';const math = require(path); // 合法
  • ESM:依赖关系在编译时确定,模块路径必须是静态字符串(不能动态生成):

ES Modules 是编译时解析
ES Modules 的 import 声明是静态的,必须写在模块的顶层(不能嵌套在条件语句中)。浏览器或 JS 引擎会在代码执行前(编译阶段)就解析所有 import 声明,构建完整的模块依赖关系图,这种静态分析能力让 ESM 支持树摇(tree-shaking)、类型检查等编译时优化。

const path = \'./math.js\';import { add } from path; // 报错!路径必须是静态的

4. 循环依赖处理

循环依赖指 A 依赖 B,B 同时依赖 A 的情况,两者处理方式不同:

CommonJS
  • 当模块循环引用时,require() 会返回已执行部分的 exports(可能不完整)。
  • 例:
// a.jsconst b = require(\'./b.js\');console.log(\'a 中 b 的值:\', b); // { b: undefined, getB: [Function] }module.exports = { a: 1 };// b.jsconst a = require(\'./a.js\');console.log(\'b 中 a 的值:\', a); // {}(此时 a 尚未导出完成)module.exports = { b: 2, getB: () => a.a // 后续可通过函数获取完整值};
ESM
  • 循环依赖时,模块会通过引用绑定保持关联,即使依赖未完全加载,也能获取到最终值。
  • 例:
// a.jsimport { b, getB } from \'./b.js\';console.log(\'a 中 b 的值:\', b); // undefined(初始值)export const a = 1;console.log(\'a 中 getB():\', getB()); // 1(获取到 a 的最终值)// b.jsimport { a } from \'./a.js\';export let b = 2;export const getB = () => a; // 引用 a 的绑定,最终会获取到 1

5. 环境支持

  • CommonJS

    • 主要用于 Node.js(默认模块系统),浏览器不原生支持(需通过 Webpack 等工具转译)。
    • 模块文件扩展名可省略(Node.js 会自动补全 .js.json 等)。
  • ESM
    • 浏览器原生支持(需在 中使用)。
    • Node.js 从 v12 开始支持(需将文件扩展名改为 .mjs,或在 package.json 中设置 \"type\": \"module\")。

6. 顶层作用域

● CommonJS:
每个模块的顶层 this 指向 module.exports,且存在 module、exports、require 等内置变量。
● ES Modules:
顶层 this 为 undefined,没有 module、exports 等变量,只能通过 import/export 操作模块。

7. 加载方式

CommonJS:
○ 同步加载:模块加载会阻塞后续代码执行,适合服务器端(Node.js),因为文件存在于本地磁盘,加载速度快。
ES Modules:
○ 异步加载:模块加载不会阻塞代码执行,适合浏览器环境(网络请求可能延迟),且支持并行加载多个模块。

总结
● CommonJS 是 Node.js 的传统模块规范,适合服务器端,支持动态加载,但不支持 Tree-shaking。
● ESM 是 ES 标准模块规范,浏览器和现代 Node.js 均支持,静态加载支持 Tree-shaking,更适合前端工程化。