在前幾天,我們已經認識了 Node.js 的基礎與專案骨架。
今天要進入一個非常重要的主題:模組系統 (Modules)。
模組是程式碼的組裝單位,幫助我們拆分功能、重複利用,避免程式碼一坨混在一起。
在 Node.js 世界裡,目前存在 兩套模組系統:
CommonJS (CJS)
Node.js 在 2009 年誕生時,瀏覽器還沒有標準化的模組語法。
為了管理大型程式碼,社群制定了 CommonJS,使用 require
/ module.exports
。
npm 上大量套件都是基於 CJS 開發的。
ES Modules (ESM)
2015 年(ES6)JavaScript 終於有了官方模組系統:import
/ export
。
它支援靜態分析、tree-shaking,並與瀏覽器保持一致。
👉CJS 是 Node.js 的傳統,ESM 是未來趨勢。
Node.js 提供幾種方式來決定 .js
檔案的解讀方式:
預設:沒有特別設定 → .js
當成 CJS。
使用副檔名:
.cjs
→ 永遠視為 CJS.mjs
→ 永遠視為 ESM設定 package.json:
{
"type": "module"
}
這樣專案裡的 .js
會預設是 ESM,要寫 CJS 就得用 .cjs
。
項目 | CommonJS (CJS) | ES Modules (ESM) |
---|---|---|
副檔名 | .cjs / .js (預設) |
.mjs 或 .js (需 package.json 設定 "type": "module" ) |
匯入 | const x = require('./x') |
import x from './x.js' |
匯出 | module.exports = ... 或 exports.a = ... |
export default ... 或 export const a = ... |
載入方式 | 同步(執行時解析) | 非同步(編譯期分析,可用 top-level await ) |
相容性 | 舊專案與大部分套件 | 新專案趨勢,與瀏覽器標準一致 |
// math.cjs
function add(a, b) {
return a + b;
}
function subtract(a, b) {
return a - b;
}
module.exports = { add, subtract };
// app.cjs
const { add, subtract } = require('./math.cjs');
console.log(add(3, 5)); // 8
console.log(subtract(10, 4)); // 6
方式一:default export
// math.mjs
export default function add(a, b) {
return a + b;
}
// app.mjs
import add from './math.mjs';
console.log(add(2, 3)); // 5
方式二:named export
// math.mjs
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
// app.mjs
import { add, subtract } from './math.mjs';
console.log(add(4, 6)); // 10
console.log(subtract(9, 2)); // 7
📌 注意:ESM 匯入相對路徑時,副檔名必須寫 .js
或 .mjs
,否則會報錯。
在實際開發專案中,常會遇到「CJS 與 ESM 混用」的情況:
在 ESM 專案裡引入 CJS:
// app.mjs (ESM)
import pkg from "lodash"; // lodash 是 CJS
console.log(pkg.camelCase("hello world"));
👉 Node.js 會自動幫你把 CJS 的 module.exports 當成 default export。
所以在 ESM 裡匯入 CJS,要用 import pkg from ...
,而不是 {}
。
在 CJS 專案裡引入 ESM:
// app.cjs
(async () => {
const esm = await import("./esm.mjs");
console.log(esm.hello());
})();
👉 在 CJS 專案裡引入 ESM,只能用 import()
,而且要在 非同步環境下。
功能 | CommonJS (CJS) | ES Modules (ESM) |
---|---|---|
匯入 | const x = require('./x') |
import x from './x.js' |
匯出(單一) | module.exports = foo |
export default foo |
匯出(多個) | exports.a = 1 |
export const a = 1 |
動態載入 | require("./x") |
const x = await import("./x.js") |
Node.js 正在逐漸與 瀏覽器標準接軌:
require
/ module.exports
。fetch
、URL
、TextEncoder
已在 Node.js 內建,與瀏覽器一致。未來的 Node.js 會越來越像瀏覽器,你只要會 JavaScript,在前端怎麼寫,後端幾乎也能照樣寫。
今天學到: