Website Performance Optimization: Resources, Bandwidth & Speed


目錄 Table of Contents

  1. 為什麼效能重要
  2. 效能衡量指標 Core Web Vitals
  3. HTML 最佳化
  4. CSS 最佳化
  5. JavaScript 最佳化
  6. 圖片與媒體最佳化
  7. 字型最佳化
  8. 網路層最佳化:HTTP、快取、壓縮
  9. 伺服器端最佳化
  10. 資料庫讀寫最佳化
  11. CDN 與邊緣運算
  12. 前端架構策略
  13. 監控與持續最佳化

1. 為什麼效能重要

1.1 使用者體驗

  • 53% 的行動用戶會在頁面載入超過 3 秒時離開(Google 研究)
  • 每延遲 100ms,Amazon 報告轉換率下降 1%
  • 頁面速度直接影響 SEO 排名(Google 於 2021 年將 Core Web Vitals 列入排名因素)

1.2 成本效益

  • 減少頻寬 = 降低雲端費用
  • 減少伺服器運算 = 減少 CPU/記憶體用量
  • 減少資源大小 = 降低使用者行動數據消耗

2. 效能衡量指標 Core Web Vitals

Google 定義的三大核心指標:

指標 全名 衡量什麼 良好 需改善
LCP Largest Contentful Paint 最大元素渲染時間 ≤ 2.5s ≤ 4.0s > 4.0s
INP Interaction to Next Paint 互動後到畫面回應 ≤ 200ms ≤ 500ms > 500ms
CLS Cumulative Layout Shift 版面位移程度 ≤ 0.1 ≤ 0.25 > 0.25

其他重要指標

指標 說明
FCP (First Contentful Paint) 首個可見內容繪製時間
TTFB (Time To First Byte) 從請求到收到第一個位元組
TTI (Time to Interactive) 頁面變為可互動的時間
TBT (Total Blocking Time) 主執行緒被阻塞的總時間

測量工具

  • Google Lighthouse — Chrome DevTools → Lighthouse 分頁
  • PageSpeed Insights — https://pagespeed.web.dev/
  • WebPageTest — https://www.webpagetest.org/
  • Chrome DevTools → Network / Performance — 詳細瀑布圖分析

3. HTML 最佳化

3.1 減少 DOM 大小

DOM 節點越多,瀏覽器渲染越慢(建議 < 1500 節點)。

<!-- ❌ 巢狀過深 -->
<div><div><div><div><p>Text</p></div></div></div></div>

<!-- ✅ 扁平化 -->
<p>Text</p>

3.2 資源載入策略

<!-- CSS: 放在 <head>,不阻塞渲染 -->
<link rel="stylesheet" href="critical.css">

<!-- 非關鍵 CSS: 延遲載入 -->
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">

<!-- JS: defer(不阻塞 HTML 解析,依序執行) -->
<script src="app.js" defer></script>

<!-- JS: async(不阻塞,但不保證順序) -->
<script src="analytics.js" async></script>

3.3 資源預載入 (Resource Hints)

<!-- Preconnect:提前建立連線 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com" crossorigin>

<!-- DNS Prefetch:提前 DNS 查詢 -->
<link rel="dns-prefetch" href="https://api.example.com">

<!-- Preload:提前載入關鍵資源 -->
<link rel="preload" href="hero.webp" as="image">
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>

<!-- Prefetch:預載下一頁資源(低優先) -->
<link rel="prefetch" href="/next-page.html">

<!-- Prerender:預先渲染下一頁 -->
<link rel="prerender" href="/likely-next-page.html">

3.4 壓縮 HTML

  • 移除多餘空白、註解
  • 工具:html-minifier-terser
npx html-minifier-terser --collapse-whitespace --remove-comments input.html -o output.html

4. CSS 最佳化

4.1 關鍵 CSS 內嵌 (Critical CSS)

將首屏需要的 CSS 直接內嵌在 <head> 中,其餘延遲載入:

<head>
    <style>
        /* 首屏關鍵樣式 */
        body { margin: 0; font-family: system-ui; }
        .hero { height: 100vh; background: #1a5fb4; }
    </style>
    <link rel="stylesheet" href="full.css" media="print" onload="this.media='all'">
</head>

4.2 移除未使用的 CSS

Chrome DevTools → Coverage 工具可檢查未使用的 CSS。

工具: - PurgeCSS — 分析 HTML 並移除未用到的 CSS - UnCSS — 類似功能

npx purgecss --css styles.css --content index.html --output cleaned.css

4.3 CSS 壓縮

npx cssnano styles.css -o styles.min.css
# 或
npx lightningcss --minify --bundle styles.css -o styles.min.css

4.4 避免昂貴的 CSS 屬性

昂貴(觸發重新渲染/佈局) 高效替代
box-shadow(大範圍) 使用圖片或 filter: drop-shadow()
border-radius + overflow 簡化巢狀
position: fixed + backdrop-filter 減少模糊範圍
頻繁改變 width/height 使用 transform: scale()
改變 top/left 使用 transform: translate()

4.5 使用 CSS 硬體加速

.animated {
    transform: translateZ(0);       /* 觸發 GPU 合成層 */
    will-change: transform, opacity; /* 預先告知瀏覽器 */
}

5. JavaScript 最佳化

5.1 程式碼分割 (Code Splitting)

只載入當前頁面需要的 JS:

// 動態 import — 按需載入
const module = await import('./heavy-module.js');

// 路由級分割(React 範例)
const Dashboard = React.lazy(() => import('./Dashboard'));

5.2 Tree Shaking

打包工具(Webpack, Rollup, Vite)可自動移除未使用的 export:

// utils.js
export function usedFunction() { /* ... */ }
export function unusedFunction() { /* ... */ }  // 打包時被移除

// app.js
import { usedFunction } from './utils.js';

5.3 壓縮與混淆

# Terser — 業界標準 JS 壓縮
npx terser app.js -o app.min.js --compress --mangle

# esbuild — 極快速壓縮
npx esbuild app.js --minify --outfile=app.min.js

5.4 避免主執行緒阻塞

// ❌ 長時間運算阻塞 UI
function heavyCalculation() {
    for (let i = 0; i < 1e9; i++) { /* ... */ }
}

// ✅ 使用 Web Worker 放到背景執行
const worker = new Worker('worker.js');
worker.postMessage({ data: largeData });
worker.onmessage = (e) => { console.log(e.data); };

5.5 減少第三方腳本影響

<!-- 延遲載入非核心第三方腳本 -->
<script src="https://analytics.example.com/script.js" async></script>

<!-- 使用 Intersection Observer 延遲載入 -->
<script>
const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            const script = document.createElement('script');
            script.src = 'widget.js';
            document.body.appendChild(script);
            observer.disconnect();
        }
    });
});
observer.observe(document.querySelector('#widget-container'));
</script>

5.6 防抖 (Debounce) 與節流 (Throttle)

// Debounce: 等待停止觸發後才執行(搜尋輸入)
function debounce(fn, delay) {
    let timer;
    return (...args) => {
        clearTimeout(timer);
        timer = setTimeout(() => fn(...args), delay);
    };
}

// Throttle: 固定間隔最多執行一次(滾動事件)
function throttle(fn, interval) {
    let lastTime = 0;
    return (...args) => {
        const now = Date.now();
        if (now - lastTime >= interval) {
            lastTime = now;
            fn(...args);
        }
    };
}

window.addEventListener('scroll', throttle(handleScroll, 100));
input.addEventListener('input', debounce(handleSearch, 300));

6. 圖片與媒體最佳化

6.1 選擇正確的格式

格式 適用場景 壓縮率 瀏覽器支援
WebP 照片、插圖 比 JPEG 小 25-34% 所有現代瀏覽器
AVIF 照片(最佳壓縮) 比 WebP 再小 20% Chrome, Firefox, Safari 16.4+
SVG 圖標、向量圖 極小 全部
JPEG 照片(回退) 中等 全部
PNG 需要透明度 較大 全部

6.2 響應式圖片

<picture>
    <!-- AVIF 優先(最小) -->
    <source srcset="image.avif" type="image/avif">
    <!-- WebP 次之 -->
    <source srcset="image.webp" type="image/webp">
    <!-- JPEG 回退 -->
    <img src="image.jpg" alt="描述" width="800" height="600" loading="lazy">
</picture>

<!-- 不同螢幕寬度提供不同大小 -->
<img
    srcset="image-400.webp 400w,
            image-800.webp 800w,
            image-1200.webp 1200w"
    sizes="(max-width: 600px) 400px,
           (max-width: 1024px) 800px,
           1200px"
    src="image-800.webp"
    alt="描述"
    width="800" height="600"
    loading="lazy"
    decoding="async"
>

6.3 懶載入 (Lazy Loading)

<!-- 原生瀏覽器懶載入 -->
<img src="photo.webp" alt="..." loading="lazy" width="400" height="300">
<iframe src="video.html" loading="lazy"></iframe>

6.4 圖片壓縮工具

工具 類型 特點
Squoosh (squoosh.app) 線上 Google 出品,支援所有格式
Sharp (npm) CLI / Node.js 速度極快
ImageOptim macOS 桌面 無損壓縮
TinyPNG 線上 PNG/JPEG 壓縮
# Sharp CLI 範例
npx sharp-cli -i input.jpg -o output.webp --webp
npx sharp-cli -i input.jpg -o output.avif --avif

6.5 始終指定圖片尺寸

避免 CLS(版面位移):

<!-- ✅ 指定 width/height -->
<img src="photo.webp" alt="..." width="800" height="600">

<!-- 或使用 CSS aspect-ratio -->
<style>
    .img-container { aspect-ratio: 4 / 3; }
</style>

7. 字型最佳化

7.1 減少字型大小

/* 只載入需要的字重 */
@font-face {
    font-family: 'CustomFont';
    src: url('font.woff2') format('woff2');  /* WOFF2 最小 */
    font-weight: 400;
    font-display: swap;  /* 先顯示系統字型,載入後替換 */
}

7.2 Unicode-range 子集化

只載入需要的字元:

/* 只載入拉丁字元 */
@font-face {
    font-family: 'CustomFont';
    src: url('font-latin.woff2') format('woff2');
    unicode-range: U+0000-00FF;
}

/* 中文字型子集化 */
@font-face {
    font-family: 'ChineseFont';
    src: url('font-cjk.woff2') format('woff2');
    unicode-range: U+4E00-9FFF;
}

7.3 Google Fonts 最佳載入

<!-- 預連線 + 指定字重 + display=swap -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;700&display=swap" rel="stylesheet">

7.4 系統字型堆疊(零網路成本)

body {
    font-family:
        -apple-system, BlinkMacSystemFont,
        'Segoe UI', 'Noto Sans TC', 'Microsoft JhengHei',
        Roboto, Oxygen, Ubuntu, sans-serif;
}

8. 網路層最佳化:HTTP、快取、壓縮

8.1 啟用壓縮

壓縮方式 壓縮率 速度 支援度
Brotli (br) 最佳(比 gzip 小 15-25%) 較慢壓縮,快速解壓 所有現代瀏覽器
Gzip (gz) 良好 快速 全部
Zstandard (zstd) 最佳平衡 極快 新興支援

Nginx 設定:

# Brotli
brotli on;
brotli_types text/html text/css application/javascript application/json image/svg+xml;
brotli_comp_level 6;

# Gzip 回退
gzip on;
gzip_types text/html text/css application/javascript application/json image/svg+xml;
gzip_min_length 1000;

8.2 HTTP 快取策略

# 靜態資源:長期快取 + 內容雜湊檔名
location ~* \.(js|css|webp|avif|woff2)$ {
    expires 1y;
    add_header Cache-Control "public, immutable";
}

# HTML:不快取或短期快取
location ~* \.html$ {
    add_header Cache-Control "no-cache";
}

# API 回應:不快取
location /api/ {
    add_header Cache-Control "no-store";
}

快取策略表:

資源類型 Cache-Control 說明
HTML no-cache 每次驗證是否更新
CSS/JS(有 hash) max-age=31536000, immutable 一年,內容變動時改檔名
圖片(有 hash) max-age=31536000, immutable 同上
API 資料 no-storemax-age=60 依資料性質而定
字型 max-age=31536000 字型很少變動

8.3 HTTP/2 與 HTTP/3

特性 HTTP/1.1 HTTP/2 HTTP/3
多路複用 ❌(每連線一個請求)
標頭壓縮 ✅ (HPACK) ✅ (QPACK)
伺服器推送
傳輸協定 TCP TCP QUIC (UDP)
隊頭阻塞 部分解決 完全解決

8.4 減少 HTTP 請求

  • 合併小型 CSS/JS 檔案
  • 使用 CSS Sprites 或 SVG Symbols 合併圖標
  • 內嵌極小資源(< 1KB 的 SVG、CSS)
  • 使用 <link rel="preload"> 優先載入關鍵資源

8.5 ETag 與條件請求

# 第一次請求
GET /style.css HTTP/2
→ 200 OK
   ETag: "abc123"

# 第二次請求(快取驗證)
GET /style.css HTTP/2
If-None-Match: "abc123"
→ 304 Not Modified(不傳送 body,節省頻寬)

9. 伺服器端最佳化

9.1 降低 TTFB

方法 效果
使用距離用戶近的伺服器/CDN 減少網路延遲
資料庫查詢最佳化(加 index) 減少伺服器處理時間
應用層快取(Redis) 避免重複運算
連線池 (Connection Pooling) 減少資料庫連線開銷
使用 HTTP/2+ 減少連線建立次數

9.2 快取層級

[用戶端快取] → [CDN 快取] → [反向代理快取] → [應用快取 (Redis)] → [資料庫]
         ←──── 越左邊越快、越便宜 ────→

9.3 Redis 快取範例

import redis
import json

cache = redis.Redis(host='localhost', port=6379)

def get_user(user_id):
    cache_key = f"user:{user_id}"
    cached = cache.get(cache_key)
    if cached:
        return json.loads(cached)  # 快取命中,極快

    user = db.query(f"SELECT * FROM users WHERE id = {user_id}")
    cache.setex(cache_key, 3600, json.dumps(user))  # 快取 1 小時
    return user

9.4 反向代理 (Reverse Proxy)

[客戶端] → [Nginx 反向代理] → [應用伺服器 (Node.js/Python)]
                   │
                   ├── 靜態檔案直接回應(不經過應用)
                   ├── 回應壓縮 (Brotli/Gzip)
                   ├── SSL 終止
                   └── 負載均衡

10. 資料庫讀寫最佳化

10.1 索引 (Index)

-- 為常用查詢欄位加索引
CREATE INDEX idx_users_email ON users (email);
CREATE INDEX idx_orders_user_date ON orders (user_id, created_at);

-- 複合索引:注意欄位順序(高選擇性優先)
CREATE INDEX idx_products_category_price ON products (category, price);

10.2 查詢最佳化

-- ❌ 全表掃描
SELECT * FROM orders WHERE YEAR(created_at) = 2025;

-- ✅ 使用範圍(可用索引)
SELECT * FROM orders
WHERE created_at >= '2025-01-01' AND created_at < '2026-01-01';

-- ❌ SELECT *(浪費頻寬)
SELECT * FROM users;

-- ✅ 只取需要的欄位
SELECT id, name, email FROM users;

-- ❌ N+1 查詢
for user in users:
    orders = db.query("SELECT * FROM orders WHERE user_id = ?", user.id)

-- ✅ JOIN 或批次查詢
SELECT u.*, o.* FROM users u
LEFT JOIN orders o ON u.id = o.user_id;

10.3 讀寫分離

[寫入請求] → [主資料庫 (Primary)]
                    │ 複製
                    ↓
[讀取請求] → [唯讀副本 (Read Replica)] × N

10.4 連線池 (Connection Pooling)

// Node.js + PostgreSQL 連線池
const { Pool } = require('pg');
const pool = new Pool({
    host: 'localhost',
    database: 'mydb',
    max: 20,              // 最大連線數
    idleTimeoutMillis: 30000,
    connectionTimeoutMillis: 2000,
});

const result = await pool.query('SELECT * FROM users WHERE id = $1', [userId]);

11. CDN 與邊緣運算

11.1 CDN (Content Delivery Network)

將靜態資源部署到全球各地的邊緣節點,讓用戶從最近的伺服器取得資源。

[用戶 (台北)] → [CDN 邊緣節點 (台北)] → [原始伺服器 (美國)]
                       ↑ 已快取 → 直接回應(< 20ms)

11.2 常見 CDN 服務

CDN 特點 免費方案
Cloudflare 全球最大免費 CDN,DDoS 防護 慷慨免費方案
AWS CloudFront 與 AWS 深度整合 有免費額度
Fastly 即時清除快取,邊緣運算 有免費額度
Vercel Edge 自動 CDN + 邊緣函數 前端框架最佳
Bunny CDN 性價比極高 付費

11.3 邊緣運算 (Edge Computing)

在 CDN 節點上執行程式碼,極度降低延遲:

// Cloudflare Workers 範例
export default {
    async fetch(request) {
        const cache = caches.default;
        let response = await cache.match(request);
        if (response) return response;

        response = await fetch(request);
        const newResponse = new Response(response.body, response);
        newResponse.headers.set('Cache-Control', 'max-age=3600');
        await cache.put(request, newResponse.clone());
        return newResponse;
    }
};

12. 前端架構策略

12.1 渲染策略比較

策略 全名 LCP TTI SEO 適用
SSG Static Site Generation 極快 極佳 部落格、文件
SSR Server-Side Rendering 中等 極佳 電商、新聞
CSR Client-Side Rendering 後台管理
ISR Incremental Static Regeneration 極快 極佳 大量頁面
Streaming SSR 分段串流 複雜頁面

12.2 Islands Architecture

只有互動部分使用 JavaScript,其餘為純 HTML:

┌──────────────────────────────────────┐
│  靜態 HTML(不需 JS)                 │
│  ┌──────────┐     ┌──────────┐      │
│  │ JS Island│     │ JS Island│      │
│  │ (互動元件)│     │ (互動元件)│      │
│  └──────────┘     └──────────┘      │
│  更多靜態 HTML...                     │
└──────────────────────────────────────┘

框架:Astro, Fresh (Deno), Qwik

12.3 Service Worker 離線快取

// service-worker.js
const CACHE_NAME = 'v1';
const ASSETS = ['/', '/styles.css', '/app.js', '/offline.html'];

self.addEventListener('install', (e) => {
    e.waitUntil(
        caches.open(CACHE_NAME).then(cache => cache.addAll(ASSETS))
    );
});

self.addEventListener('fetch', (e) => {
    e.respondWith(
        caches.match(e.request).then(cached => cached || fetch(e.request))
    );
});

13. 監控與持續最佳化

13.1 效能預算 (Performance Budget)

資源類型 預算上限
HTML ≤ 50 KB
CSS(總計) ≤ 100 KB
JS(總計) ≤ 300 KB
圖片(每張) ≤ 200 KB
字型(總計) ≤ 100 KB
首次載入總大小 ≤ 1 MB

13.2 監控工具

工具 用途
Google Search Console Core Web Vitals 報告
Lighthouse CI CI/CD 中自動跑效能測試
Sentry 前端錯誤與效能監控
Web Vitals (npm) 程式內收集真實用戶數據
// 收集真實用戶 Core Web Vitals
import { onLCP, onINP, onCLS } from 'web-vitals';

onLCP(metric => sendToAnalytics('LCP', metric));
onINP(metric => sendToAnalytics('INP', metric));
onCLS(metric => sendToAnalytics('CLS', metric));

13.3 最佳化檢查清單

  • [ ] 啟用 Brotli/Gzip 壓縮
  • [ ] 圖片使用 WebP/AVIF + lazy loading
  • [ ] CSS/JS 壓縮 + tree shaking
  • [ ] 設定適當的 Cache-Control 標頭
  • [ ] 使用 CDN
  • [ ] 字型使用 font-display: swap + preload
  • [ ] 關鍵 CSS 內嵌
  • [ ] JS 使用 deferasync
  • [ ] 圖片指定 width/height
  • [ ] 資料庫查詢加索引
  • [ ] 啟用 HTTP/2+
  • [ ] 設定效能預算

速查總結

層級 最佳化方法 預期效果
HTML 減少 DOM、defer/async、preload 減少解析時間
CSS Critical CSS、PurgeCSS、壓縮 減少渲染阻塞
JS Code splitting、tree shaking、壓縮 減少下載量與執行時間
圖片 WebP/AVIF、響應式、lazy loading 減少 50-80% 圖片大小
字型 WOFF2、子集化、swap 減少字型載入延遲
網路 Brotli、快取、HTTP/2+ 減少傳輸量與延遲
伺服器 Redis、連線池、反向代理 降低 TTFB
資料庫 索引、讀寫分離、批次查詢 加速讀寫
CDN 邊緣節點、邊緣運算 全球低延遲