技術 7 分鐘閱讀

網站效能優化指南:讓你的網站飛快

網站效能優化指南:讓你的網站飛快

前言

網站速度直接影響使用者體驗和 SEO 排名。Google 的研究指出,頁面載入時間超過 3 秒,53% 的行動裝置使用者會直接離開。這篇文章從前端到後端,分享我在接案專案中常用的效能優化技巧。

先量測再優化

Core Web Vitals

Google 定義了三個核心指標:

  • LCP(Largest Contentful Paint):最大內容繪製,應在 2.5 秒以內
  • INP(Interaction to Next Paint):互動到下一次繪製,應在 200ms 以內
  • CLS(Cumulative Layout Shift):累計版面位移,應低於 0.1

測量工具

  • Lighthouse:Chrome DevTools 內建
  • PageSpeed Insights:Google 的線上分析工具
  • WebPageTest:更詳細的效能測試

圖片優化

圖片通常佔網頁總容量的 50% 以上,是最值得優化的地方。

使用現代格式

<picture>
  <source srcset="/images/hero.avif" type="image/avif" />
  <source srcset="/images/hero.webp" type="image/webp" />
  <img src="/images/hero.jpg" alt="首頁橫幅" />
</picture>

AVIF 比 WebP 再小 20-30%,但瀏覽器支援度稍低,用 <picture> 標籤做漸進式支援。

響應式圖片

<img
  srcset="
    /images/product-400.webp 400w,
    /images/product-800.webp 800w,
    /images/product-1200.webp 1200w
  "
  sizes="(max-width: 640px) 400px, (max-width: 1024px) 800px, 1200px"
  src="/images/product-800.webp"
  alt="商品圖片"
  loading="lazy"
  decoding="async"
/>

圖片壓縮自動化

在 Vite 專案中使用 vite-plugin-imagemin

// vite.config.ts
import imagemin from 'vite-plugin-imagemin';

export default defineConfig({
  plugins: [
    imagemin({
      webp: { quality: 80 },
      avif: { quality: 65 },
    }),
  ],
});

前端優化

程式碼分割

React 的 lazySuspense 可以按需載入元件:

const ProductPage = lazy(() => import('./pages/ProductPage'));
const AdminDashboard = lazy(() => import('./pages/AdminDashboard'));

function App() {
  return (
    <Suspense fallback={<LoadingSpinner />}>
      <Routes>
        <Route path="/products" element={<ProductPage />} />
        <Route path="/admin" element={<AdminDashboard />} />
      </Routes>
    </Suspense>
  );
}

Tree Shaking

確保 import 是按需引入:

// 不好:引入整個套件
import _ from 'lodash';
_.debounce(fn, 300);

// 好:只引入需要的函式
import debounce from 'lodash/debounce';
debounce(fn, 300);

字型優化

@font-face {
  font-family: 'Noto Sans TC';
  src: url('/fonts/NotoSansTC-Regular.woff2') format('woff2');
  font-display: swap;
  unicode-range: U+4E00-9FFF; /* 只載入中文字元 */
}

font-display: swap 讓瀏覽器先用系統字型顯示文字,等自訂字型載入後再替換,避免 FOIT(Flash of Invisible Text)。

預載入關鍵資源

<head>
  <link rel="preload" href="/fonts/NotoSansTC.woff2" as="font" crossorigin />
  <link rel="preload" href="/images/hero.webp" as="image" />
  <link rel="preconnect" href="https://api.example.com" />
</head>

CSS 優化

移除未使用的 CSS

Tailwind CSS 的 PurgeCSS 會自動處理這件事。確認 content 設定正確:

// tailwind.config.js
module.exports = {
  content: [
    './src/**/*.{js,ts,jsx,tsx}',
    './index.html',
  ],
};

避免大量動畫

CSS 動畫只用在 transformopacity 上,這兩個屬性由 GPU 處理,效能最好:

/* 好:使用 transform */
.card:hover {
  transform: translateY(-4px);
  transition: transform 0.2s ease;
}

/* 不好:觸發 layout 重新計算 */
.card:hover {
  margin-top: -4px;
}

後端優化

Laravel 快取策略

// 設定快取
Route::middleware('cache.headers:public;max_age=3600')->group(function () {
    Route::get('/api/categories', [CategoryController::class, 'index']);
});

// 資料庫查詢快取
$products = Cache::remember('products:featured', 3600, function () {
    return Product::where('featured', true)
        ->with('category')
        ->get();
});

壓縮回應

在 Nginx 中啟用 Gzip:

gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml;
gzip_min_length 1000;
gzip_comp_level 6;

CDN 部署

靜態資源放到 CDN 上,減少伺服器壓力,也能讓全球使用者都有好的載入速度:

ASSET_URL=https://cdn.example.com

進階技巧

Service Worker 快取

// service-worker.js
const CACHE_NAME = 'v1';
const STATIC_ASSETS = [
  '/',
  '/css/app.css',
  '/js/app.js',
  '/images/logo.webp',
];

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

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      return response || fetch(event.request);
    })
  );
});

虛擬列表

當列表項目超過數百個時,使用虛擬列表只渲染可見區域的元素:

import { useVirtualizer } from '@tanstack/react-virtual';

function ProductList({ products }: { products: Product[] }) {
  const parentRef = useRef<HTMLDivElement>(null);

  const virtualizer = useVirtualizer({
    count: products.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 80,
  });

  return (
    <div ref={parentRef} style={{ height: '600px', overflow: 'auto' }}>
      <div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
        {virtualizer.getVirtualItems().map((virtualItem) => (
          <div
            key={virtualItem.key}
            style={{
              position: 'absolute',
              top: 0,
              transform: `translateY(${virtualItem.start}px)`,
              height: `${virtualItem.size}px`,
            }}
          >
            <ProductCard product={products[virtualItem.index]} />
          </div>
        ))}
      </div>
    </div>
  );
}

效能優化清單

  • 圖片使用 WebP/AVIF 格式
  • 啟用圖片懶載入
  • 程式碼分割(Route-based)
  • 壓縮 JavaScript 和 CSS
  • 啟用 Gzip/Brotli 壓縮
  • 設定適當的快取標頭
  • 使用 CDN
  • 字型優化(swap、subset)
  • 減少第三方腳本
  • 資料庫查詢優化

結語

效能優化是一個持續的過程,不需要一次做完所有事情。先用 Lighthouse 找出最大的瓶頸,從影響最大的地方開始優化。記住,使用者體驗永遠是最重要的。

#效能優化 #前端 #Core Web Vitals #SEO
Thousand Lab

Thousand Lab