模組化(Import / Export)
為什麼要模組化?
比喻:模組就像樂高積木 🧱
把程式碼拆成小塊,每塊負責一件事。 需要時組裝起來,不需要時拆掉。
ES Modules 基本語法
Named Export(具名匯出)
// utils.js
export function add(a, b) {
return a + b;
}
export function subtract(a, b) {
return a - b;
}
export const PI = 3.14159;
// app.js
import { add, subtract, PI } from './utils.js';
console.log(add(1, 2)); // 3
console.log(PI); // 3.14159
// 重新命名
import { add as sum } from './utils.js';
console.log(sum(1, 2)); // 3
// 匯入全部
import * as utils from './utils.js';
console.log(utils.add(1, 2)); // 3
Default Export(預設匯出)
// Calculator.js
export default class Calculator {
add(a, b) { return a + b; }
subtract(a, b) { return a - b; }
}
// app.js — default export 不用大括號,名字隨便取
import Calculator from './Calculator.js';
import Calc from './Calculator.js'; // 也可以
let calc = new Calculator();
console.log(calc.add(1, 2)); // 3
混合使用
// api.js
export default class ApiClient { /* ... */ }
export function formatUrl(path) { /* ... */ }
export const BASE_URL = "/api";
import ApiClient, { formatUrl, BASE_URL } from './api.js';
動態匯入
// 需要時才載入(節省初始載入時間)
async function loadChart() {
let { Chart } = await import('./chart.js'); // ← 動態匯入
let chart = new Chart("#canvas");
chart.render(data);
}
// 條件匯入
if (needsAdmin) {
let { AdminPanel } = await import('./admin.js');
}
在 HTML 中使用
<!-- type="module" 才能使用 import/export -->
<script type="module" src="app.js"></script>
<!-- 模組的特性 -->
<!-- 1. 自動使用嚴格模式 -->
<!-- 2. 有自己的作用域(不會汙染全域) -->
<!-- 3. 只會執行一次(多次 import 不會重複執行) -->
模組組織最佳實踐
project/
├── index.html
├── js/
│ ├── app.js ← 進入點
│ ├── api/
│ │ ├── client.js ← API 請求封裝
│ │ └── endpoints.js ← API 路徑常數
│ ├── components/
│ │ ├── Header.js ← UI 元件
│ │ └── Card.js
│ ├── utils/
│ │ ├── format.js ← 格式化工具
│ │ └── validate.js ← 驗證工具
│ └── constants.js ← 全域常數
// 用 index.js 統一匯出(barrel export)
// utils/index.js
export { formatDate, formatCurrency } from './format.js';
export { validateEmail, validatePhone } from './validate.js';
// 使用時
import { formatDate, validateEmail } from './utils/index.js';