[Lv3] Nuxt 3 效能優化:Bundle Size、SSR 速度與圖片優化
Nuxt 3 效能優化全攻略:從 Bundle Size 瘦身、SSR 速度優化到圖片載入策略,打造極致效能體驗。
1. 面試回答主軸
- Bundle Size 優化:分析 (
nuxi analyze)、拆分 (SplitChunks)、Tree Shaking、延遲載入 (Lazy Loading)。 - SSR 速度優化 (TTFB):Redis 快取、Nitro Cache、減少阻塞式 API 呼叫、Streaming SSR。
- 圖片優化:
@nuxt/image、WebP 格式、CDN、Lazy Loading。 - 大量資料優化:虛擬滾動 (Virtual Scrolling)、無限滾動 (Infinite Scroll)、分頁 (Pagination)。
2. 如何減少 Nuxt 3 的 Bundle Size?
2.1 診斷工具
首先,必須知道瓶頸在哪裡。使用 nuxi analyze 來視覺化 Bundle 結構。
npx nuxi analyze
這會產生一個報告,顯示哪些套件佔用了最大空間。
2.2 優化策略
1. Code Splitting (代碼拆分)
Nuxt 3 預設已經基於路由 (Route-based) 做 Code Splitting。但對於大型套件(如 ECharts, Lodash),我們需要手動優化。
Nuxt Config 配置 (Vite/Webpack):
// nuxt.config.ts
export default defineNuxtConfig({
vite: {
build: {
rollupOptions: {
output: {
manualChunks(id) {
// 將 node_modules 中的大型套件拆分出來
if (id.includes('node_modules')) {
if (id.includes('lodash')) return 'lodash';
if (id.includes('echarts')) return 'echarts';
}
},
},
},
},
},
});
2. Tree Shaking & 按需引入
確保只引入需要的模組,而不是整個套件。
// ❌ 錯誤:引入整個 lodash
import _ from 'lodash';
_.debounce(() => {}, 100);
// ✅ 正確:只引入 debounce
import debounce from 'lodash/debounce';
debounce(() => {}, 100);
// ✅ 推薦:使用 vueuse (Vue 專用且 Tree-shakable)
import { useDebounceFn } from '@vueuse/core';
3. 組件 Lazy Loading
對於非首屏需要的組件,使用 Lazy 前綴進行動態導入。
<template>
<div>
<!-- 只有當 show 為 true 時才會載入該組件代碼 -->
<LazyHeavyComponent v-if="show" />
</div>
</template>
4. 移除不必要的 Server-side 套件
確保只在 Server 端使用的套件(如資料庫驅動、fs 操作)不會被打包到 Client 端。Nuxt 3 會自動處理 .server.ts 結尾的檔案,或使用 server/ 目錄。
3. 如何優化 SSR 速度 (TTFB)?
3.1 為什麼 TTFB 會過長?
TTFB (Time To First Byte) 是 SSR 效能的關鍵指標。過長的原因通常是:
- API 回應慢:Server 需要等待後端 API 回傳資料才能渲染 HTML。
- 串行請求:多個 API 請求依序執行,而非並行。
- 繁重的計算:Server 端執行了過多 CPU 密集型任務。
3.2 優化方案
1. Server-Side Caching (Nitro Cache)
使用 Nitro 的快取功能,將 API 回應或渲染結果快取起來。
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// 首頁快取 1 小時 (SWR: Stale-While-Revalidate)
'/': { swr: 3600 },
// 產品頁快取 10 分鐘
'/products/**': { swr: 600 },
// API 快取
'/api/**': { cache: { maxAge: 60 } },
},
});
2. 並行請求 (Parallel Fetching)
使用 Promise.all 並行發送多個請求,而不是 await 一個接一個。
// ❌ 慢:串行執行 (總時間 = A + B)
const { data: user } = await useFetch('/api/user');
const { data: posts } = await useFetch('/api/posts');
// ✅ 快:並行執行 (總時間 = Max(A, B))
const [{ data: user }, { data: posts }] = await Promise.all([
useFetch('/api/user'),
useFetch('/api/posts'),
]);
3. 延遲非關鍵資料 (Lazy Fetching)
首屏不需要的資料,可以在 Client 端再載入 (lazy: true),避免阻塞 SSR。
// 評論資料不需要 SEO,可以在 Client 端載入
const { data: comments } = await useFetch('/api/comments', {
lazy: true,
server: false, // 甚至完全不在 Server 端執行
});
4. Streaming SSR (實驗性)
Nuxt 3 支援 HTML Streaming,可以邊渲染邊回傳,讓使用者更快看到內容。
4. Nuxt 3 圖片優化
4.1 使用 @nuxt/image
官方模組 @nuxt/image 是最佳解,提供:
- 自動格式轉換:自動轉為 WebP/AVIF。
- 自動縮放:根據螢幕大小產生對應尺寸圖片。
- Lazy Loading:內建懶加載。
- CDN 整合:支援 Cloudinary, Imgix 等多種 Provider。
4.2 實作範例
npm install @nuxt/image
// nuxt.config.ts
export default defineNuxtConfig({
modules: ['@nuxt/image'],
image: {
// 預設選項
format: ['webp'],
},
});
<template>
<!-- 自動轉換為 webp,寬度 300px,並啟用 lazy load -->
<NuxtImg
src="/hero.jpg"
format="webp"
width="300"
loading="lazy"
placeholder
/>
</template>
5. 大量資料的分頁與滾動
5.1 方案選擇
針對大量資料(如 10,000 筆商品),主要有三種策略,需考量 SEO:
| 策略 | 適合場景 | SEO 友善度 |
|---|---|---|
| 傳統分頁 (Pagination) | 電商列表、文章列表 | ⭐⭐⭐⭐⭐ (最佳) |
| 無限滾動 (Infinite Scroll) | 社交動態、圖片牆 | ⭐⭐ (需特殊處理) |
| 虛擬滾動 (Virtual Scroll) | 複雜報表、超長列表 | ⭐ (內容不在 DOM 中) |