CommonJS 与 ES 模块规范的区别
JavaScript 的模块化经历了从无到有的过程,CommonJS 和 ES 模块(ESM) 是两种主流的模块规范。它们的核心差异体现在设计目标、语法、运行机制和适用环境上。
1. 设计目标
| CommonJS | ES 模块 (ESM) |
|---|
| 为 Node.js 设计,解决服务器端模块化问题 | ECMAScript 官方标准,为浏览器和 Node.js 统一模块化方案 |
| 动态加载,适用于同步文件读取场景 | 静态加载,支持编译时优化(如 Tree Shaking) |
2. 语法对比
CommonJS
- 导出模块:
module.exports = function() { };
exports.a = 1;
exports.b = 2;
- 导入模块:
const lib = require('./lib');
const { a, b } = require('./lib');
ES 模块
- 导出模块:
export default function() { };
export const a = 1;
export const b = 2;
- 导入模块:
import lib from './lib.js';
import { a, b } from './lib.js';
import('./lib.js').then(module => { });
3. 加载机制
| CommonJS | ES 模块 |
|---|
动态加载:require() 可在代码任意位置调用 | 静态加载:import 必须位于模块顶层 |
| 同步执行:模块加载时立即执行代码 | 异步执行:模块解析与执行可分离 |
| 值的拷贝:导入的是模块导出的值的副本 | 值的引用:导入的是模块导出的实时绑定 |
4. 运行时差异
CommonJS
ES 模块
5. 环境支持
| CommonJS | ES 模块 |
|---|
| Node.js 默认模块系统 | 浏览器原生支持(需 <script type="module">) |
| 不支持浏览器环境 | Node.js 从 v13.2.0 开始支持(需 .mjs 扩展名或 "type": "module") |
6. 其他关键区别
| 特性 | CommonJS | ES 模块 |
|---|
顶层 this | 指向 module.exports | 指向 undefined |
| 代码执行时机 | 加载时立即执行 | 预处理后按需执行 |
| 优化潜力 | 动态加载难以静态分析 | 支持 Tree Shaking、代码压缩优化 |
| 互操作性 | 可通过 import() 加载 ESM | 需通过 require() 或工具转换 |
7. 如何选择?
- Node.js 环境:
- 传统项目或需要动态加载时,使用 CommonJS。
- 新项目或需要跨平台兼容性时,优先选择 ES 模块。
- 浏览器环境:
- 必须使用 ES 模块(通过 Webpack 等工具也可转换 CommonJS)。
示例:混合使用两种模块
在 Node.js 中同时支持两种规范:
import { createRequire } from 'module';
const require = createRequire(import.meta.url);
const fs = require('fs');
总结
- CommonJS:简单灵活,适合服务器端同步场景,但缺乏静态优化。
- ES 模块:标准化、静态化,支持跨平台和编译优化,是未来趋势。
根据项目需求选择合适的规范,现代工具链(如 Babel、Webpack)可无缝兼容两者。