加载机制
-
CommonJS:
- 使用
require
函数来加载模块,这是同步的,意味着在require
调用完成并返回之前,代码会被阻塞。 - 模块在首次加载时会被执行,并且其导出的内容会被缓存,后续的
require
调用将直接返回缓存的结果。
- 使用
-
ES6 Modules (ESM):
- 使用
import
语句来导入模块,这本质上是异步的,因为模块解析和加载可以在其他代码执行的同时进行。 - ESM 在解析阶段确定模块依赖关系,这意味着它们可以更好地与现代构建工具和优化策略(如树摇动)配合工作。
- 使用
值的处理
-
CommonJS:
- 导出的是值的拷贝,这意味着如果你改变了一个导出的对象或函数,外部引用不会看到这些变化。
-
ES6 Modules:
- 导出的是值的引用,意味着如果模块内部的值发生了变化,所有导入该值的地方都会看到最新的状态。
静态分析
- ES6 Modules 支持静态分析,因为
import
语句必须在文件的顶层声明,这使得构建工具可以更早地了解模块的依赖关系,从而有助于代码优化和错误检测。
动态导入
- ES6 Modules 支持动态导入,使用
import()
表达式,它返回一个 Promise,这在运行时动态加载模块时非常有用。
兼容性
- CommonJS 主要在 Node.js 环境中使用,而 ES6 Modules 初始主要用于浏览器环境,但现在 Node.js 也原生支持 ESM。
实际场景示例
- 在 Node.js 中,你可能仍然会看到大量的 CommonJS 模块,尤其是在较老的项目或者那些还没有完全迁移到 ESM 的生态系统中。
- 在 浏览器 中,ES6 Modules 更常见,因为它们提供了更好的性能和可维护性。然而,由于并非所有浏览器都完全支持 ESM,因此可能需要构建工具(如 Webpack 或 Rollup)来转换 ESM 到兼容的格式,或者使用 polyfills。
随着标准的发展,新的工具和库越来越倾向于使用 ES6 Modules,但是 CommonJS 仍然在很多现有的项目和库中占有一席之地。在选择模块系统时,应该考虑项目的特定需求、团队的熟悉程度以及所使用的工具链。
语法区别
CommonJS
- 导入模块:使用
require
函数。 - 导出模块:使用
module.exports
或exports
。
例如,假设有一个名为 math.js
的模块,它导出了一个函数 add
:
// math.js
module.exports = {
add: function(a, b) {
return a + b;
}
};
然后在另一个文件中,你可以这样导入它:
// app.js
var math = require('./math');
console.log(math.add(1, 2)); // 输出 3
ES6 Modules (ESM)
- 导入模块:使用
import
语句。 - 导出模块:使用
export
语句。
同样的例子,使用ES6模块的话:
// math.js
export function add(a, b) {
return a + b;
}
在另一个文件中导入它:
// app.js
import { add } from './math';
console.log(add(1, 2)); // 输出 3