特性 | CommonJS (module.exports , exports ) | ES6 (export ) |
---|---|---|
语法 | module.exports = ... | export const/let/var name = ...; |
exports.name = ... | export function myFunc() { ... } | |
const myVar = ...; module.exports = myVar; | export { name1, name2 as alias, ... }; | |
module.exports = { name: ..., func: ... }; | export default someValue; | |
导出类型 | 导出一个可以是任何值的单一对象 | 可以导出多个命名的绑定 (live bindings) 和一个默认导出 |
导入语法 | const module = require('./module'); | import { name1, alias } from './module'; |
const { name } = require('./module'); | import * as module from './module'; | |
import defaultExport from './module'; | ||
import './module'; (仅执行模块代码) | ||
加载时机 | 运行时加载 (动态) | 编译时加载 (静态) |
静态分析 | 不易进行静态分析 | 支持静态分析,便于 Tree Shaking |
循环依赖处理 | 可能出现问题,加载部分导出或错误 | 更好地处理循环依赖 |
适用环境 | 主要用于 Node.js 环境 | 现代浏览器和 Node.js (需配置或 .mjs 文件) |
“Live Binding” | 不支持 | 支持。导入的变量会随着原始模块的改变而更新 |
详细解释:
-
语法差异:
- CommonJS: 使用
module.exports
直接赋值或通过操作exports
对象来导出。导入时使用require()
,返回的是模块导出的值。 - ES6: 使用
export
关键字显式标记需要导出的变量、函数、类等。可以导出多个命名的成员,也可以使用export default
导出一个默认值。导入时使用import
关键字,可以选择性地导入需要的命名成员或导入默认导出。
- CommonJS: 使用
-
导出类型:
- CommonJS: 最终导出的总是一个单一的对象。即使你通过
exports
添加了多个属性,require()
返回的也是这个包含这些属性的对象。 - ES6: 可以导出多个独立的、命名的绑定。这意味着导入的变量和原始模块导出的变量之间存在实时的连接。如果原始模块导出的变量发生变化,导入的变量也会随之更新 (live binding)。同时,ES6 模块还支持一个匿名或命名的默认导出。
- CommonJS: 最终导出的总是一个单一的对象。即使你通过
-
加载时机:
- CommonJS: 模块在运行时加载。
require()
语句在代码执行到那一行时才会去加载和执行对应的模块。这意味着依赖关系是在运行时确定的。 - ES6: 模块在编译时进行静态分析。
import
语句在代码执行之前就被处理,模块的依赖关系在编译阶段就确定了。这为静态分析和优化(如 Tree Shaking)提供了基础。
- CommonJS: 模块在运行时加载。
-
静态分析和 Tree Shaking:
- CommonJS: 由于是运行时加载,难以进行可靠的静态分析,因此不支持 Tree Shaking(移除未使用的代码)。
- ES6: 静态分析使得编译器可以知道模块导出了哪些成员以及哪些成员被导入和使用,从而可以安全地移除未被使用的代码,减小最终的 bundle 大小。
-
循环依赖处理:
- CommonJS: 在处理循环依赖时可能会比较复杂,可能会遇到只加载到部分导出的情况,或者在某些情况下导致错误。
- ES6: ES6 模块在处理循环依赖方面更加健壮,它允许模块先执行一部分代码,然后再处理循环依赖,通常能更好地解决这类问题。
-
适用环境:
- CommonJS: 是 Node.js 的原生模块系统,在 Node.js 环境中广泛使用。
- ES6: 是 JavaScript 语言的标准模块系统,被现代浏览器广泛支持。Node.js 也从较新的版本开始支持 ES6 模块,但通常需要使用
.mjs
文件扩展名或者在package.json
中进行配置 ("type": "module"
)。
-
“Live Binding” (实时绑定):
- CommonJS: 不支持 live binding。
require()
导入的是导出值的拷贝(浅拷贝)。如果原始模块导出的值在之后发生变化,导入的变量不会自动更新。 - ES6: 支持 live binding。
import
导入的是对导出变量的引用。当原始模块中导出的变量改变时,所有导入该变量的地方都会同步更新。
- CommonJS: 不支持 live binding。
总结来说:
ES6 模块系统是 JavaScript 语言层面的标准化模块方案,它在语法、静态分析、Tree Shaking 和处理循环依赖等方面都比 CommonJS 更加先进和强大。虽然 CommonJS 在 Node.js 环境中仍然被广泛使用,但随着 JavaScript 生态的发展,ES6 模块正逐渐成为主流。在现代前端开发中,ES6 模块几乎是唯一的选择。在 Node.js 项目中,也越来越倾向于使用 ES6 模块,尤其是在新的项目中。