语法差异
- ES6模块:使用
import
和export
关键字。例如:
// 导出
export const name = 'John';
export function greet() {
console.log('Hello');
}
// 导入
import { name, greet } from './module.js';
- CommonJS模块:使用
exports
或module.exports
导出,require
导入。例如:
// 导出
const name = 'John';
function greet() {
console.log('Hello');
}
exports.name = name;
exports.greet = greet;
// 或者
module.exports = {
name,
greet
};
// 导入
const { name, greet } = require('./module.js');
加载机制差异
- ES6模块:是编译时加载(静态加载),即在代码编译阶段就确定模块的依赖关系和导入导出的变量,编译时就可以分析出模块的依赖关系,能进行静态优化,且只在首次加载时执行模块代码,之后再次导入使用缓存。例如:
// moduleA.js
export const value = 1;
// main.js
import { value } from './moduleA.js';
console.log(value);
- CommonJS模块:是运行时加载(动态加载),在运行到
require
语句时才会去加载模块并执行模块代码,每次require
都会执行模块代码并返回导出的值。例如:
// moduleA.js
let value = 1;
exports.getValue = function() {
return value;
};
// main.js
const { getValue } = require('./moduleA.js');
console.log(getValue());
作用域差异
- ES6模块:每个模块都有自己独立的作用域,模块顶级的变量、函数等不会污染全局作用域。例如:
// module.js
let count = 0;
export function increment() {
count++;
return count;
}
// main.js
import { increment } from './module.js';
console.log(increment());
// count变量不会暴露到其他模块作用域
- CommonJS模块:同样每个模块有独立作用域,
exports
、require
、module
等在模块作用域内有效,不会污染全局。例如:
// module.js
let count = 0;
exports.increment = function() {
count++;
return count;
};
// main.js
const { increment } = require('./module.js');
console.log(increment());
// count变量不会暴露到其他模块作用域
应用场景
- 适合ES6模块的场景:
- 浏览器端应用:现代浏览器原生支持ES6模块,如构建单页应用(SPA)时,使用ES6模块能利用其静态加载的优势进行优化,提升性能。例如使用Vue、React等框架开发前端应用时,可直接使用ES6模块进行代码拆分和组织。
- 追求代码静态分析和优化的场景:如在打包工具(Webpack等)中,ES6模块的静态特性便于进行摇树优化(Tree - shaking),去除未使用的代码,减小打包体积。
- 适合CommonJS模块的场景:
- Node.js服务器端应用:Node.js从一开始就采用CommonJS规范,在Node.js环境中使用CommonJS模块非常自然和方便,如开发Node.js的后端服务、命令行工具等。例如Express框架的中间件开发,大量使用CommonJS模块来组织代码。
- 动态加载场景:如果需要根据运行时条件动态加载模块,CommonJS的运行时加载机制更合适。例如根据不同的用户权限动态加载不同的模块。