ES Module(模組)

介紹

ES 模組(ES Module,ECMAScript Module,簡稱 ESM)是 JavaScript 在 ES6(ECMAScript 2015)中引入的一種標準模組系統,主要用來解決:

  • 全域命名衝突
  • 變數污染
  • 依賴管理困難等問題

瀏覽器環境的 ESM 特性

  • 模組內變數具有區域作用域(不會污染全域)
  • 模組以異步方式加載(等同於加上 defer
  • 必須透過 HTTP(S) 協定執行,無法直接從 file:// 路徑載入
  • 可搭配原生瀏覽器 cache 機制,有效利用瀏覽器快取

ES 模組的基本概念

  • 每個 .js.mjs 檔案皆為一個模組
  • 可使用 export 導出變數、函式或類別
  • 可使用 import 導入其他模組的導出內容
  • 一個模組可同時包含命名導出與預設導出(但僅能有一個預設導出)

exportimport 的語法

命名導出(Named Export)

// utils.js
export const name = "Vue";
export function greet() {
    return `Hello, ${name}!`;
}

// main.js
import { name, greet } from './utils.js';

console.log(name);        // "Vue"
console.log(greet());     // "Hello, Vue!"

命名導出可導出多個項目,導入時必須使用相同名稱,並包在 {} 中。


預設導出(Default Export)

// math.js
export default function add(x, y) {
    return x + y;
}

// main.js
import sum from './math.js';

console.log(sum(2, 3));   // 5

每個模組僅能有一個預設導出。導入時可自行命名,不需要使用 {}


在瀏覽器中使用 ES 模組

<!DOCTYPE html>
<html lang="zh-TW">
<head>
    <meta charset="UTF-8">
    <title>ES Modules</title>
</head>
<body>
    <script type="module">
        import { name, greet } from './utils.js';
        console.log(greet());
    </script>
</body>
</html>

type="module" 表示這段 <script> 是 ES 模組,模組會自動延後執行(類似加上 defer),且支援 import 語法。

⚠️ 注意:需透過 HTTP 伺服器執行(如 Live Server、Vite dev server、localhost),否則將因 CORS 或 file:// 限制導致 import 失敗。


在 Node.js 中使用 ES 模組

自 Node.js 12 起支援 ESM(Node 14+ 更完整),需符合以下條件之一:

方法一:在 package.json 設定為模組模式

{
  "type": "module"
}
// utils.js
export function sayHello() {
    return "Hello, Node.js ESM!";
}

// main.js
import { sayHello } from './utils.js';

console.log(sayHello());
node main.js

方法二:使用 .mjs 副檔名

// utils.mjs
export function sayHello() {
    return "Hello, Node.js ESM!";
}

// main.mjs
import { sayHello } from './utils.mjs';

console.log(sayHello());
node main.mjs

動態匯入(Dynamic Import)

ESM 支援 import() 語法,可在程式執行階段動態載入模組,回傳 Promise

const { default: add } = await import('./math.js');
console.log(add(1, 2));

用途:

  • 延遲載入(Lazy loading)
  • 根據條件動態選擇模組
  • 在非頂層使用匯入邏輯

注意事項:ESM 與 CommonJS (require) 的相容性限制

Node.js 原本使用 CommonJS 模組系統(require / module.exports),而 ESM 使用 import / export,兩者在某些情境下無法直接混用:

CommonJS 中無法使用 import

// ❌ 不可行
import { something } from './mod.js';

// ✅ 應使用:
const { something } = require('./mod.js');

ESM 中無法使用 require

// ❌ 不可行
const fs = require('fs'); // ReferenceError

// ✅ 可用 createRequire:
import { createRequire } from 'node:module';
const require = createRequire(import.meta.url);
const fs = require('fs');

導入 CommonJS 模組時,需注意 default 包裝

// math.cjs
module.exports = {
  add: (a, b) => a + b
};

// main.mjs
import math from './math.cjs';
console.log(math.add(1, 2)); // ✅ OK,透過 default 封裝

相容性表格

情境 是否支援 備註
CommonJS 使用 require 載入 ESM 不支援,會噴錯
CommonJS 使用 import 除非設定 type: module
ESM 使用 import 載入 CommonJS 可行,但須透過 default 包裝
ESM 使用 require 須使用 createRequire()

總結

類型 說明
命名導出 可導出多個,導入時需使用相同名稱
預設導出 每個模組只能一個,導入時可自訂名稱
type="module" 瀏覽器或 Node.js 指定為 ES 模組
.mjs 明確標示為模組檔,常用於 Node.js 中
require 限制 不能在 ESM 中直接使用
import() 動態匯入模組,返回 Promise

參考資源