
概念概覽
flowchart LR subgraph 輸入["原始檔案"] JS["JS / TS"] CSS["CSS / SCSS"] IMG["圖片資源"] HTML["HTML"] end subgraph 打包工具["Bundler"] Webpack["Webpack"] Vite["Vite"] end subgraph 輸出["優化結果"] Bundle["bundle.js<br/>壓縮合併"] Styles["styles.css<br/>前綴 + 壓縮"] Assets["optimized assets<br/>圖片壓縮"] end JS --> 打包工具 CSS --> 打包工具 IMG --> 打包工具 HTML --> 打包工具 打包工具 --> Bundle 打包工具 --> Styles 打包工具 --> Assets
前端開發面臨的問題
1. 模組化問題
早期 JavaScript 沒有模組系統,所有變數都在全域作用域:
<!-- 傳統方式:手動管理載入順序 -->
<script src="jquery.js"></script>
<script src="utils.js"></script> <!-- 依賴 jquery -->
<script src="component.js"></script> <!-- 依賴 utils -->
<script src="app.js"></script> <!-- 依賴全部 -->
<!-- 問題:
1. 順序錯誤就壞掉
2. 全域變數污染
3. 依賴關係不明確
-->現代模組化:
// utils.js
export function formatDate(date) { ... }
// app.js
import { formatDate } from './utils.js';
import React from 'react'; // npm 套件2. 瀏覽器相容性
// 新語法
const sum = (a, b) => a + b;
const user = { ...defaultUser, name: 'John' };
const result = await fetchData();
// 舊瀏覽器不支援!需要轉換成:
var sum = function(a, b) { return a + b; };
var user = Object.assign({}, defaultUser, { name: 'John' });3. 效能問題
<!-- 太多 HTTP 請求 -->
<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="module3.js"></script>
<!-- ... 50 個檔案 -->
<!-- 檔案太大 -->
<script src="bundle.js"></script> <!-- 5MB 未壓縮 -->打包工具解決的問題
┌─────────────────────────────────────────────────────────┐
│ 打包工具 │
│ │
│ 輸入 輸出 │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ ES6+ 語法 │ │ ES5 相容 │ │
│ │ JSX/TSX │ ──→ │ 純 JS │ │
│ │ SCSS/Less │ │ 純 CSS │ │
│ │ 多個模組 │ │ 單一 Bundle │ │
│ │ 原始圖片 │ │ 優化圖片 │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────┘
主要打包工具比較
| 工具 | 特點 | 適用場景 |
|---|---|---|
| Webpack | 功能最完整、生態系成熟 | 大型專案 |
| Vite | 開發速度極快、設定簡單 | 現代專案(推薦) |
| Rollup | 打包效果最佳 | 套件開發 |
| esbuild | 速度最快 | 簡單打包 |
| Parcel | 零設定 | 小型專案 |
Webpack 基礎
核心概念
// webpack.config.js
module.exports = {
// 入口:從哪開始分析依賴
entry: './src/index.js',
// 輸出:打包後的檔案
output: {
filename: 'bundle.[contenthash].js',
path: path.resolve(__dirname, 'dist'),
},
// Loader:處理非 JS 檔案
module: {
rules: [
{
test: /\.jsx?$/,
use: 'babel-loader', // 轉換 ES6+
},
{
test: /\.css$/,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(png|jpg|gif)$/,
type: 'asset/resource',
},
],
},
// Plugin:擴充功能
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
}),
],
};打包流程
入口分析 → 依賴解析 → Loader 轉換 → 程式碼優化 → 輸出 Bundle
src/
├── index.js ──→ dist/
├── App.jsx ├── bundle.js
├── styles.css ├── styles.css
└── logo.png └── logo.abc123.png
Vite - 現代開發體驗
為什麼 Vite 這麼快?
Webpack 開發模式:
啟動 → 打包所有檔案 → 啟動 Dev Server → 等待...(可能數分鐘)
Vite 開發模式:
啟動 → 直接啟動 Dev Server → 按需編譯(毫秒級)
Vite 利用瀏覽器原生 ES Modules:
<!-- Vite 開發時直接使用 ESM -->
<script type="module" src="/src/main.js"></script>Vite 設定
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
server: {
port: 3000,
proxy: {
'/api': 'http://localhost:8080',
},
},
build: {
outDir: 'dist',
minify: 'esbuild',
},
});常見優化功能
1. 程式碼分割(Code Splitting)
// 動態載入,按需載入
const Dashboard = lazy(() => import('./Dashboard'));
const Settings = lazy(() => import('./Settings'));
// 打包結果
dist/
├── main.js // 主要程式碼
├── Dashboard.js // 獨立 chunk
└── Settings.js // 獨立 chunk2. Tree Shaking
// utils.js
export function used() { ... }
export function unused() { ... } // 不會被打包
// app.js
import { used } from './utils'; // 只引入 used3. 壓縮與優化
// 原始碼
function calculateTotal(items) {
let total = 0;
for (const item of items) {
total += item.price * item.quantity;
}
return total;
}
// 壓縮後
function calculateTotal(t){let e=0;for(const n of t)e+=n.price*n.quantity;return e}開發 vs 生產
| 階段 | 目標 | 設定 |
|---|---|---|
| 開發 | 快速反饋 | Source Map、HMR、不壓縮 |
| 生產 | 效能優先 | 壓縮、Tree Shaking、快取 |
// package.json
{
"scripts": {
"dev": "vite", // 開發模式
"build": "vite build", // 生產打包
"preview": "vite preview" // 預覽生產結果
}
}常見問題
什麼時候不需要打包工具?
- 簡單的靜態網頁
- 使用 CDN 載入函式庫
- 小型原型專案
該選 Webpack 還是 Vite?
新專案 → Vite(更快、更簡單)
舊專案維護 → 繼續用 Webpack
需要高度自訂 → Webpack
開發套件 → Rollup