📋 目錄
效能不是「做出來的」,是「設計進去的」。很多團隊在產品上線後才發現 Core Web Vitals 不及格,這時候要救效能,代價已經是設計階段的十倍以上。2026 年的效能優化,已經從「有就好」變成「SEO 必須」。本文提供一個完整的框架,幫你從測量、診斷到實作,建立系統化的效能優化流程。
前端工程師為什麼要關心效能
Google 在 2021 年把 Core Web Vitals 納入 SEO 排名信號。四年過去,這件事對搜尋排名的影響越來越大。對於電子商務、新聞內容網站、工具類 SaaS,LCP(最大內容繪製)每差 1 秒,轉換率可能下降 20-30%。
但效能的影響不只是 SEO。INP(互動到下一次繪製)的問題,會讓你的 UI 感覺「卡卡的」——使用者在輸入表單時,點擊按鈕後要等幾百毫秒才有反應,這種延遲累積起來,會讓使用者覺得你的產品「不夠專業」。
2026 年的今天,行動裝置的網速和 CPU 能力差異極大。你的旗艦機跑流暢的頁面,在印度或東南亞的低階 Android 手機上可能需要 5 秒鐘才能互動。效能優化不是「給有錢人看的網站」做的,而是「要服務所有用戶」的產品必須投資的事。
Core Web Vitals 2026:三個你必須知道的指標
Core Web Vitals 是 Google 定義的三個使用者體驗指標,2026 年的標準已經從 2021 年的「指導」變成了「事實上的及格線」。
1. LCP(Largest Contentful Paint):你的頁面多久才「看起來好了」
LCP 測量的是從使用者請求頁面到「最大內容元素」被繪製到螢幕上的時間。「最大內容元素」通常是:
- Hero image(主要橫幅圖)
- 頂部的大標題文字
- 視訊的某一幀
2026 年的 LCP 及格標準:
| 等級 | LCP 時間 |
|---|---|
| Good(良好) | ≤ 2.5 秒 |
| Needs Improvement(需要改進) | 2.5s - 4s |
| Poor(不良) | > 4 秒 |
LCP 的主要瓶頸:
- TTFB(Time to First Byte)太慢:伺服器回應時間過長
- Render-Blocking Resources(渲染阻擋資源):CSS 和 JS 檔案阻止頁面繪製
- 圖片未優化:圖片檔案過大、未使用現代格式、沒有 preload
<!-- ✅ 正確:preload LCP 圖片 -->
<link rel="preload" as="image" href="/hero.webp" fetchpriority="high" />
<!-- ❌ 錯誤:讓瀏覽器自己發現 LCP 圖片 -->
<img src="/hero.webp" />
2. INP(Interaction to Next Paint):你的頁面多久才「能互動」
INP 測量的是從使用者互動(點擊、鍵盤輸入)到瀏覽器繪製下一幀的時間。INP 取代了 2024 年之前的 FID(First Input Delay),因為 INP 更全面地反映了頁面的互動延遲。
2026 年的 INP 及格標準:
| 等級 | INP 時間 |
|---|---|
| Good | ≤ 200ms |
| Needs Improvement | 200ms - 500ms |
| Poor | > 500ms |
INP 的主要瓶頸:
- Long Tasks(長任務):JavaScript 在主執行緒執行時間過長,阻塞了瀏覽器的回應
- 大型同步計算:大量資料在主執行緒做 filtering、sorting、serialization
- 第三方腳本:Analytics、Chat widget、Cookie banner 搶佔主執行緒
// ❌ 錯誤:大量計算阻塞主執行緒
function processLargeDataset(data) {
// 這個迴圈可能執行數百毫秒,讓 UI 完全卡住
return data
.filter(item => item.active)
.sort((a, b) => b.score - a.score)
.map(item => enrichItem(item));
}
// ✅ 正確:用 Web Worker 把計算移到背景執行緒
function processInWorker(data) {
return new Promise((resolve) => {
const worker = new Worker('/worker.js');
worker.postMessage({ data });
worker.onmessage = (e) => resolve(e.data);
});
}
3. CLS(Cumulative Layout Shift):你的版面會不會突然跑掉
CLS 測量的是頁面在載入過程中,元素位置「跳掉」了多少。典型的 CLS 問題包括:
- 圖片沒有設定
width和height,造成版面跳動 - 網頁字體(Web Fonts)載入後文字替換,造成段落跳位
- 廣告或嵌入內容動態載入,撐開了原本固定的位置
2026 年的 CLS 及格標準:
| 等級 | CLS 分數 |
|---|---|
| Good | ≤ 0.1 |
| Needs Improvement | 0.1 - 0.25 |
| Poor | > 0.25 |
<!-- ✅ 正確:明確設定圖片尺寸,防止版面跳動 -->
<img
src="/product.jpg"
width="400"
height="300"
alt="Product image"
style="aspect-ratio: 4/3;"
/>
<!-- ✅ 正確:字體載入策略 -->
<style>
@font-face {
font-family: 'Noto Sans TC';
font-display: optional; /* 如果字體沒及時載入,就用系統字體,不閃爍 */
/* 或者用 swap:先用系統字,載好後置換 */
}
</style>
系統化效能優化:從測量到實作
效能優化的第一步不是「做優化」,而是「建立測量基準」。沒有測量,你不知道優化的方向對不对。
Step 1:建立效能基準
# 使用 Lighthouse CI 在 CI/CD pipeline 裡建立效能基準
npx lighthouse https://your-site.com --output=json --output-path=./lighthouse-report.json
# 查看關鍵分數
cat ./lighthouse-report.json | jq '.categories.performance.score'
建議在專案根目錄建立 performance-budget.json:
{
"budgets": [
{
"resourceSizes": [
{ "resourceType": "total", "budget": 500 },
{ "resourceType": "script", "budget": 150 },
{ "resourceType": "image", "budget": 200 },
{ "resourceType": "font", "budget": 50 }
],
"resourceCounts": [
{ "resourceType": "third-party", "budget": 10 }
]
}
]
}
Step 2:測量三大瓶頸
1. TTFB(伺服器回應時間)
# 測量 TTFB
curl -o /dev/null -s -w "TTFB: %{time_starttransfer}s\n" https://your-site.com
TTFB > 600ms 通常表示:
- 沒有 CDN 或 CDN 設定不當
- 伺服器計算時間過長(資料庫查詢、N+1)
- 快取策略不正確
2. 圖片優化(影響 LCP)
# 檢查圖片格式和大小
# AVIF > WebP > JPEG > PNG > GIF
# 將圖片轉換為 WebP
cwebp -q 80 input.jpg -o output.webp
# 將圖片轉換為 AVIF(更高壓縮率)
# 需要 ImageMagick 或 libavif
圖片格式選擇指南:
| 格式 | 透明度 | 動畫 | 適用場景 | 瀏覽器支援 |
|---|---|---|---|---|
| AVIF | ✅ | ❌ | 照片、大圖 | 主流瀏覽器 |
| WebP | ✅ | ❌ | 通用圖片 | 主流瀏覽器 |
| JPEG | ❌ | ❌ | 照片、縮圖 | 全部 |
| PNG | ✅ | ❌ | 需要透明背景 | 全部 |
| GIF | ❌ | ✅ | 簡單動畫 | 全部 |
3. JavaScript 執行時間(影響 INP)
// 在 Chrome DevTools 的 Console 裡測量 Long Tasks
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.duration > 50) { // > 50ms 的任務被視為 Long Task
console.warn(`Long Task detected: ${entry.duration.toFixed(2)}ms`, entry);
}
}
});
observer.observe({ type: 'longtask', buffered: true });
程式碼分割:減少初始載入成本
Route-Based Splitting
現代框架都支援 route-based splitting(按路由分割),這是減少初始 JavaScript 負擔最有效的方式:
// React + React Router v6 的 code splitting
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
// ✅ 只有訪問這個路由時,才會載入這個 chunk
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>載入中...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
Component-Level Splitting
對於大型元件,可以使用 component-level splitting,只在使用者需要互動時才載入:
// 只在點擊按鈕時才載入 Modal
const HeavyModal = lazy(() => import('./components/HeavyModal'));
function App() {
const [showModal, setShowModal] = useState(false);
return (
<>
<button onClick={() => setShowModal(true)}>開啟設定</button>
<Suspense fallback={null}>
{showModal && <HeavyModal onClose={() => setShowModal(false)} />}
</Suspense>
</>
);
}
Dynamic Import 的實際應用
// ❌ 錯誤:所有工具函式在初始就全部載入
import * as utils from './utils'; // 200KB
import * as charts from './charts'; // 300KB
// ✅ 正確:只用到的才載入
async function handleData(data) {
const utils = await import('./utils');
const result = utils.process(data);
return result;
}
效能預算:讓優化成為開發流程的一部分
效能問題最常見的原因不是「不知道怎麼優化」,而是「優化了一次,上線幾週後又慢慢變回去了」。解決這個問題的方法是把效能預算(Performance Budget)寫進 CI/CD pipeline。
Lighthouse CI 的實際設定
# .lighthouserc.yml
ci:
collect:
url:
- https://staging.your-site.com
assert:
preset: lighthouse:recommended
assertions:
categories:performance:
min: 0.9
first-contentful-paint:
maxNumericValue: 2000
largest-contentful-paint:
maxNumericValue: 2500
interactive:
maxNumericValue: 3500
cumulative-layout-shift:
maxNumericValue: 0.1
圖片預算
{
"resourceSizes": [
{ "resourceType": "image", "budget": 200 },
{ "resourceType": "script", "budget": 150 },
{ "resourceType": "total", "budget": 500 }
]
}
第三方腳本管控
第三方腳本(Analytics、Tag Manager、Chat widget)是效能的隱形殺手。建議對每一個第三方腳本問三個問題:
- 這個腳本真的必要嗎?
- 可以延後載入(defer)嗎?
- 可以放到 Web Worker 嗎?
<!-- 典型的 Analytics 腳本——可以加 async 或 defer -->
<!-- async:頁面不必等這個腳本,但沒法保證執行順序 -->
<!-- defer:等頁面解析完才執行,不阻擋渲染 -->
<!-- ❌ 阻塞渲染 -->
<script src="https://analytics.com/script.js"></script>
<!-- ✅ 非阻塞 -->
<script async src="https://analytics.com/script.js"></script>
<!-- 或 -->
<script defer src="https://analytics.com/script.js"></script>
2026 年的新趨勢
React Server Components 減少 Client-Side JS
React Server Components(RSC)讓元件在伺服器端直接 render 成 HTML,不需要傳送 JavaScript 到客戶端。這對於大量展示型內容的頁面,節省 JavaScript 效果顯著。
// app/page.tsx — 這個元件在伺服器端執行
// 客戶端只收到 HTML,不需要這段 JS bundle
async function ProductPage({ id }) {
const product = await db.product.findUnique({ where: { id } });
return (
<div>
<h1>{product.name}</h1>
{/* 這張圖也不需要 client-side fetch,伺服器直接嵌入 URL */}
<img src={product.imageUrl} alt={product.name} />
</div>
);
}
Edge Rendering 降低延遲
Edge Rendering(邊緣運算)把頁面 render 的工作放到 CDN edge node(邊緣節點),比傳統的 origin 伺服器更接近使用者。Vercel Edge、Cloudflare Workers、AWS Lambda@Edge 都支援這個模式。
// Cloudflare Worker 範例:Edge 端即時轉換 HTML
export default {
async fetch(request) {
const cache = caches.default;
const cached = await cache.match(request);
if (cached) {
return cached;
}
const response = await fetch(request);
const modified = new HTMLRewriter()
.on('img', new ImageOptimizer())
.transform(response);
await cache.put(request, modified.clone());
return modified;
}
};
結語:效能是團隊紀律,不是單次專案
前端效能優化不是「做一次就完了」的事。每次新增功能、代碼重構、引入新函式庫,都有可能是效能倒退的時機。
把 Core Web Vitals 分數當作團隊的 KPI 之一,在 CI/CD pipeline 建立效能預算,Lighthouse 分數低於 0.9 就 block merge。讓「效能」這件事,在團隊的工作流程裡有一個固定的位置。
延伸閱讀
- Vite 6 完整升級指南 — 建構工具的效能優化
- Astro 5.0 全面解析 — 靜態網站的效能優勢
本文是「2026 前端工具與實踐」系列文章之一。如果你有興趣看更深入的圖片優化攻略或效能監控工具推薦,歡迎留言。