在 Node.js 中,模塊化是開發應用程序的核心概念,它使得代碼可以按照功能模塊進行分割,易于維護、復用和擴展。Node.js 支持兩種模塊化規范:
CommonJS(CJS):這是 Node.js 最初使用的模塊化規范。
ECMAScript Modules(ESM):這是現代 JavaScript 的官方模塊化規范,自 ECMAScript 2015(ES6)引入。
1.?CommonJS 模塊化規范
CommonJS 是 Node.js 早期就支持的模塊化標準,允許在服務端使用模塊。在 CommonJS 中,每個文件都被視為一個獨立的模塊。你可以通過 module.exports 導出模塊內容,并使用 require() 函數引入模塊。
1.1.?CommonJS 基本用法
導出模塊:使用 module.exports 或 exports。
引入模塊:使用 require()。
定義模塊:
// math.js - 定義模塊
function add(a, b) {return a + b;
}function subtract(a, b) {return a - b;
}// 使用 module.exports 導出模塊
module.exports = {add,subtract,
};// 或者也可以使用 exports(兩者作用相同)
exports.add = add;
exports.subtract = subtract;
引入模塊:
// main.js - 引入并使用模塊
const math = require('./math');console.log(math.add(5, 3)); // 輸出:8
console.log(math.subtract(5, 3)); // 輸出:2
1.2.?CommonJS 特點
同步加載:require() 是同步的,這意味著模塊會在需要時同步加載。這在服務端是可以接受的,但在瀏覽器中不夠高效。
模塊緩存:加載的模塊會被緩存,因此多次 require() 同一個模塊時,模塊只會被加載一次。
2.?ECMAScript Modules 規范
隨著 JavaScript 語言的發展,ECMAScript Modules(ESM)被引入,成為 JavaScript 官方標準的模塊系統。Node.js 從版本 12.17 開始支持 ESM,Node.js 通過引入 .mjs 文件擴展名和 package.json 中的 "type": "module" 字段來實現對 ESM 的支持。
2.1.?ECMAScript Modules 基本用法
導出模塊:使用 export 關鍵字。
引入模塊:使用 import 關鍵字。
定義模塊:
// math.mjs - 定義模塊
export function add(a, b) {return a + b;
}export function subtract(a, b) {return a - b;
}
導入模塊:
// main.mjs - 引入并使用模塊
import { add, subtract } from './math.mjs';console.log(add(5, 3)); // 輸出:8
console.log(subtract(5, 3)); // 輸出:2
2.2.?ESM 特點
靜態解析:ESM 是靜態的,這意味著在代碼編譯時就能確定模塊的依賴關系(相較于 CommonJS 的動態加載)。
異步加載:import 支持異步加載,這在瀏覽器中更高效,特別是當你需要懶加載模塊時。
嚴格模式:所有的 ECMAScript 模塊都默認處于嚴格模式("use strict")。
不允許動態導入和導出:ESM 不支持像 CommonJS 那樣的動態 require,必須在頂層進行 import/export。
3.?文件后綴與模塊加載規則
3.1.?CommonJS 文件后綴
對于 CommonJS 模塊,文件通常使用 .js 后綴。如果你在 Node.js 中編寫 CommonJS 模塊,直接使用 .js 文件即可。
3.2.?ECMAScript 模塊的文件后綴
在 Node.js 中,可以通過以下兩種方式啟用 ESM:
使用 .mjs 文件后綴:如果文件擴展名是 .mjs,Node.js 會將其視為 ESM。
配置 package.json:在項目的 package.json 中設置 "type": "module",這樣即使文件是 .js 后綴,Node.js 也會將其作為 ESM 處理。
3.3.?如何區分 CommonJS 和 ESM
Node.js 根據以下規則來確定模塊系統:
CommonJS:默認情況下,Node.js 中所有 .js 文件都被視為 CommonJS 模塊,除非另有指定。
ESM:當你使用 .mjs 擴展名,或者在 package.json 中指定 "type": "module",所有的 .js 文件都會被視為 ESM 模塊。
4.?require 和 import 互操作性
盡管 Node.js 支持 CommonJS 和 ESM 兩種規范,但兩者在一起使用時需要注意一些限制:
4.1.?從 CommonJS 中引入 ESM
從 CommonJS 文件中引入 ESM 模塊是有一定限制的。require() 無法直接加載 ESM 模塊,必須使用 import() 函數(它是一個異步函數)。
// 在 CommonJS 模塊中動態引入 ESM 模塊
(async () => {const math = await import('./math.mjs');console.log(math.add(5, 3));
})();
4.2.?從 ESM 中引入 CommonJS
ESM 模塊可以直接通過 import 引入 CommonJS 模塊,因為 Node.js 會將 CommonJS 模塊包裝成 ESM 格式以供使用。
// 在 ESM 中引入 CommonJS 模塊
import math from './math.js'; // 假設 math.js 是一個 CommonJS 模塊
console.log(math.add(5, 3));
5.?選擇 CommonJS 還是 ESM?
在實際開發中,選擇使用 CommonJS 還是 ESM 取決于幾個因素:
兼容性:如果你要支持舊的 Node.js 版本(12 之前)或使用大量依賴的第三方庫(特別是歷史遺留的),CommonJS 可能更合適。
未來發展:ESM 是 JavaScript 官方標準,未來會有更多的支持,因此如果是新項目,推薦使用 ESM。
生態系統:目前 Node.js 生態中的大多數庫仍然使用 CommonJS,但越來越多的庫開始遷移到 ESM。