中文

探索 CommonJS 和 ES Modules 之间的区别,这是 JavaScript 中两种主要的模块系统,包含现代 Web 开发的实用示例和见解。

模块系统:CommonJS vs. ES Modules - 全面指南

在不断发展的 JavaScript 开发世界中,模块化是构建可扩展和可维护应用程序的基石。历史上,两种模块系统一直占据主导地位:CommonJS 和 ES Modules (ESM)。了解它们之间的区别、优点和缺点对于任何 JavaScript 开发人员来说都至关重要,无论是在使用 React、Vue 或 Angular 等框架的前端工作,还是在使用 Node.js 的后端工作。

什么是模块系统?

模块系统提供了一种将代码组织成称为模块的可重用单元的方法。每个模块封装了一个特定的功能,并且仅公开其他模块需要使用的部分。这种方法促进了代码重用、降低了复杂性并提高了可维护性。可以将模块视为构建块;每个块都有一个特定的用途,您可以将它们组合起来创建更大、更复杂的结构。

使用模块系统的优点:

CommonJS:Node.js 标准

CommonJS 成为 Node.js 的标准模块系统,Node.js 是用于服务器端开发的热门 JavaScript 运行时环境。它旨在解决在创建 Node.js 时 JavaScript 中缺少内置模块系统的问题。Node.js 采用了 CommonJS 作为其组织代码的方式。这一选择对如何在服务器端构建 JavaScript 应用程序产生了深远的影响。

CommonJS 的主要特性:

CommonJS 语法:

以下是 CommonJS 的使用示例:

模块 (math.js):

// math.js
function add(a, b) {
 return a + b;
}

function subtract(a, b) {
 return a - b;
}

module.exports = {
 add: add,
 subtract: subtract
};

用法 (app.js):

// app.js
const math = require('./math');

console.log(math.add(5, 3)); // 输出:8
console.log(math.subtract(10, 4)); // 输出:6

CommonJS 的优点:

CommonJS 的缺点:

ES Modules (ESM):标准化的 JavaScript 模块系统

ES Modules (ESM) 是 JavaScript 的官方标准化模块系统,随 ECMAScript 2015 (ES6) 一起引入。它们旨在提供一种一致高效的方式来组织 Node.js 和浏览器中的代码。ESM 为 JavaScript 语言本身带来了原生模块支持,从而消除了对外部库或构建工具来处理模块化的需求。

ES Modules 的主要特性:

ES Modules 语法:

以下是 ES Modules 的使用示例:

模块 (math.js):

// math.js
export function add(a, b) {
 return a + b;
}

export function subtract(a, b) {
 return a - b;
}

// 或者,另一种方法:
// function add(a, b) {
//  return a + b;
// }
// function subtract(a, b) {
//  return a - b;
// }
// export { add, subtract };

用法 (app.js):

// app.js
import { add, subtract } from './math.js';

console.log(add(5, 3)); // 输出:8
console.log(subtract(10, 4)); // 输出:6

命名导出 vs. 默认导出:

ES Modules 支持命名导出和默认导出。命名导出允许您从模块中导出多个具有特定名称的值。默认导出允许您将单个值作为模块的默认导出导出。

命名导出示例 (utils.js):

// utils.js
export function formatCurrency(amount, currencyCode) {
 // 根据货币代码格式化金额
 // 示例:formatCurrency(1234.56, 'USD') 可能会返回 '$1,234.56'
 // 实现取决于所需的格式和可用的库
 return new Intl.NumberFormat('en-US', { style: 'currency', currency: currencyCode }).format(amount);
}

export function formatDate(date, locale) {
 // 根据区域设置格式化日期
 // 示例:formatDate(new Date(), 'fr-CA') 可能会返回 '2024-01-01'
 return new Intl.DateTimeFormat(locale).format(date);
}
// app.js
import { formatCurrency, formatDate } from './utils.js';

const price = formatCurrency(19.99, 'EUR'); // 欧洲
const today = formatDate(new Date(), 'ja-JP'); // 日本

console.log(price); // 输出:€19.99
console.log(today); // 输出:(根据日期而异)

默认导出示例 (api.js):

// api.js
const api = {
 fetchData: async (url) => {
 const response = await fetch(url);
 return response.json();
 }
};

export default api;
// app.js
import api from './api.js';

api.fetchData('https://example.com/data')
 .then(data => console.log(data));

ES Modules 的优点:

ES Modules 的缺点:

CommonJS vs. ES Modules:详细比较

以下表格总结了 CommonJS 和 ES Modules 之间的主要区别:

特性 CommonJS ES Modules
导入语法 require() import
导出语法 module.exports export
加载 同步 异步(在浏览器中),同步/异步(在 Node.js 中)
静态分析
原生浏览器支持
主要用例 Node.js(历史上) 浏览器和 Node.js(现代)

实用示例和用例

示例 1:创建可重用的实用程序模块(国际化)

假设您正在构建一个需要支持多种语言的 Web 应用程序。您可以创建一个可重用的实用程序模块来处理国际化 (i18n)。

ES Modules (i18n.js):

// i18n.js
const translations = {
 'en': {
 'greeting': 'Hello, world!'
 },
 'fr': {
 'greeting': 'Bonjour, le monde !'
 },
 'es': {
 'greeting': '¡Hola, mundo!'
 }
};

export function getTranslation(key, language) {
 return translations[language][key] || key;
}
// app.js
import { getTranslation } from './i18n.js';

const language = 'fr'; // 示例:用户选择了法语
const greeting = getTranslation('greeting', language);
console.log(greeting); // 输出:Bonjour, le monde !

示例 2:构建模块化 API 客户端 (REST API)

与 REST API 交互时,您可以创建一个模块化的 API 客户端来封装 API 逻辑。

ES Modules (apiClient.js):

// apiClient.js
const API_BASE_URL = 'https://api.example.com';

async function get(endpoint) {
 const response = await fetch(`${API_BASE_URL}${endpoint}`);
 if (!response.ok) {
 throw new Error(`HTTP error! status: ${response.status}`);
 }
 return response.json();
}

async function post(endpoint, data) {
 const response = await fetch(`${API_BASE_URL}${endpoint}`, {
 method: 'POST',
 headers: {
 'Content-Type': 'application/json'
 },
 body: JSON.stringify(data)
 });
 if (!response.ok) {
 throw new Error(`HTTP error! status: ${response.status}`);
 }
 return response.json();
}

export { get, post };
// app.js
import { get, post } from './apiClient.js';

get('/users')
 .then(users => console.log(users))
 .catch(error => console.error('Error fetching users:', error));

post('/users', { name: 'John Doe', email: 'john.doe@example.com' })
 .then(newUser => console.log('New user created:', newUser))
 .catch(error => console.error('Error creating user:', error));

从 CommonJS 迁移到 ES Modules

从 CommonJS 迁移到 ES Modules 可能是一个复杂的过程,尤其是在大型代码库中。以下是需要考虑的一些策略:

Node.js 和 ES Modules:

Node.js 已经发展到完全支持 ES Modules。您可以在 Node.js 中使用 ES Modules,方法是:

选择正确的模块系统

在 CommonJS 和 ES Modules 之间进行选择取决于您的特定需求以及您正在开发的环境:

结论

了解 CommonJS 和 ES Modules 之间的区别对于任何 JavaScript 开发人员来说都至关重要。虽然 CommonJS historically 一直是 Node.js 的标准,但由于其标准化性质、性能优势和对静态分析的支持,ES Modules 正迅速成为浏览器和 Node.js 的首选。通过仔细考虑项目的需求以及您正在开发的环境,您可以选择最适合您要求的模块系统,并构建可扩展、可维护且高效的 JavaScript 应用程序。

随着 JavaScript 生态系统的不断发展,了解最新的模块系统趋势和最佳实践对于取得成功至关重要。继续尝试 CommonJS 和 ES Modules,并探索各种可用的工具和技术,以帮助您构建模块化和可维护的 JavaScript 代码。