ES6模块化是ECMAScript 2015引入的官方模块化标准,而CommonJS是早期Node.js生态中广泛使用的模块化规范,两者在设计理念和语法上存在明显差异,ES6模块化通过更简洁的语法和静态分析特性,逐步实现了对CommonJS的替代。

语法层面的替代方式
导出功能的替代
CommonJS使用module.exports或者exports导出模块内容,ES6模块化则使用export关键字,分为命名导出和默认导出两种形式。
CommonJS的导出示例:
// CommonJS导出
const add = (a, b) => a + b;
const subtract = (a, b) => a - b;
module.exports = {
add,
subtract
};
// 也可以使用exports导出
exports.multiply = (a, b) => a * b;
对应的ES6模块化命名导出示例:
// ES6命名导出
export const add = (a, b) => a + b;
export const subtract = (a, b) => a - b;
// 也可以统一导出
const multiply = (a, b) => a * b;
export { multiply };
如果是默认导出,CommonJS直接赋值给module.exports,ES6使用export default:
// CommonJS默认导出
module.exports = (name) => `Hello ${name}`;
// ES6默认导出
export default (name) => `Hello ${name}`;
导入功能的替代
CommonJS使用require函数导入模块,ES6模块化使用import关键字,导入方式对应不同的导出形式。
CommonJS的导入示例:
// CommonJS导入
const { add, subtract } = require('./math');
const greet = require('./greet');
对应的ES6模块化导入示例:
// ES6导入命名导出内容
import { add, subtract } from './math.js';
// 导入默认导出内容
import greet from './greet.js';
// 也可以导入全部内容作为对象
import * as mathUtils from './math.js';
核心特性的差异与替代优势
ES6模块化相比CommonJS有几个核心优势,这也是它能够替代CommonJS的重要原因:
- 静态分析:ES6模块化的导入导出语句是静态的,在编译阶段就能确定依赖关系,支持tree shaking优化,而CommonJS的
require是动态执行的,无法在编译阶段做依赖分析。 - 值引用而非拷贝:ES6模块化导出的是值的引用,模块内部修改导出值会影响导入方,而CommonJS导出的是值的拷贝,模块内部修改不会影响已经导入的内容。
- 浏览器原生支持:ES6模块化是语言标准,现代浏览器可以直接通过
<script type="module">标签支持,不需要额外的打包工具,而CommonJS只能在Node.js环境或者经过打包工具转换后才能在浏览器运行。
实际项目中的替代方案
Node.js环境的替代
Node.js从较新版本开始已经支持ES6模块化,只需要在package.json中设置"type": "module",就可以直接使用ES6模块化语法,替代CommonJS。
原来的CommonJS项目package.json:
{
"name": "demo-project",
"version": "1.0.0",
"main": "index.js"
}
修改为支持ES6模块化:
{
"name": "demo-project",
"version": "1.0.0",
"type": "module",
"main": "index.js"
}
如果项目中同时存在两种模块化语法,Node.js会根据文件后缀区分,.mjs后缀的文件默认使用ES6模块化,.cjs后缀的文件默认使用CommonJS,方便逐步迁移。
前端项目的替代
前端项目中使用ES6模块化替代CommonJS非常简单,现在的主流打包工具如Webpack、Vite等都默认支持ES6模块化,只需要将项目中的require和module.exports替换为对应的import和export语法即可,打包工具会自动处理兼容问题。
兼容处理方案
如果需要在不支持ES6模块化的环境中运行代码,可以使用Babel等转译工具,将ES6模块化语法转换为CommonJS语法,保证代码的兼容性。同时也可以在代码中做环境判断,动态选择模块化方式:
// 兼容两种模块化方式
if (typeof module !== 'undefined' && module.exports) {
// CommonJS环境
module.exports = myFunction;
} else {
// ES6模块化环境
export default myFunction;
}
不过这种方式会增加代码复杂度,建议在项目统一模块化规范后逐步淘汰兼容代码。