跳至主要内容

[Node] CommonJS Modules and ES Modules

ES Modules

keywords: .mjs, .cjs

在 NodeJS 13 之後支援 ESModules 的寫法,使用前需要進行前置作業,方法包括下述兩種:

  • 改變整個專案的預設值:在 package.json 中加入 { "type": "module" },這時候全部的檔案都會套用 ESModules
  • 針對單一檔案:有些時候,可能會希望直接指定某檔案所使用的 module system,這時候可以透過「副檔名」來達到忽略 package.json 中 type 設定的效果。如果該檔案需要使用 Common JS 的話,可以把原本的 .js 改成 .cjs;如果該檔案需要使用 ES Module 的話,這可以把原本的 .js 改成 .mjs
提示

可以想成副檔名改成 .mjs.cjs 的情況下,能夠 override 掉 package.jsontype 的設定。

要特別留意的是,如果使用了 ES Module,則:

  • 在 import 模組時,路徑的部分需要把完整的副檔名寫出來,例如:
// 如果在 package.json 中使用 { "type": "module" }
import { sum } from './math.js';

// 如果使用的是副檔名為 .mjs
import { sum } from './math.mjs';
// 資料來源:https://antfu.me/posts/publish-esm-and-cjs
// 在 CJS 中
const { default: pkg } = await import('esm-only-package');
無法在 CJS 中使用 ESM

如果你在 CJS 中錯誤 import 了 ESM,你會看到這樣的錯誤訊息:「Error [ERR_REQUIRE_ESM]: require() of ES Module esm-only-package not supported...」(可以參考這篇:「Ship ESM & CJS in one Package」。

CommonJS Modules

匯入與匯出變數

// 匯出
exports.foo = 'foo';
exports.bar = 'bar';

// 匯入
const cjs = require('./modules'); // { foo: 'foo', bar: 'bar' }

等價於下面的寫法:

// 匯出
// Common JS 匯出的是「物件」,所以可以使用 key-value pair
module.exports = {
foo: 'foo',
bar: 'bar',
};

// 匯入
const cjs = require('./modules'); // { foo: 'foo', bar: 'bar' }

匯出與匯入函式

// greet.js

function greet() {
console.log('Hello');
}
greet();

module.exports = greet;
// app.js
const greet = require('./greet');

greet(); // 可以使用

或者

// 匯出
const sum = (a, b) => a + b;
const multiply = (a, b) => a + b;
module.exports.sum = sum;
module.exports.multiply = multiply;

module.exports = { sum, multiply };
// 匯入
const math = require('./math');
const sum = require('./math').sum;
const multiply = require('./math').multiply;

math.sum(3, 5);
math.multiply(3, 5);

沒有匯出的函式無法使用

// greet.js

function greet() {
console.log('Hello');
}
greet();

執行 app.js 時會出現 'Hello',但是在 app.js 中並沒有辦法呼叫到 greet() 這個函式

// app.js
require('./greet');

greet(); // ReferenceError: greet is not defined