[Lv2] SSR 實作:Data Fetching 與 SEO Meta 管理
在 Nuxt 3 專案中,實作 SSR 資料載入與 SEO Meta Tags 動態管理,確保搜尋引擎能正確索引動態路由頁面。
1. 面試回答主軸
- Data Fetching 策略:使用
useFetch/useAsyncData在 Server Side 預先載入資料,確保 SEO 內容完整。 - 動態 Meta Tags:使用
useHead根據資料動態生成 SEO meta tags,支援動態路由頁面。 - 效能優化:實作 request deduplication、server-side caching,區分 SSR/CSR 頁面。
2. useFetch / useAsyncData 的正確使用
2.1 為什麼需要 SSR Data Fetching?
問題情境:
- 動態路由頁面(如:
/products/[id])需要從 API 載入資料 - 如果只在客戶端載入,搜尋引擎無法看到完整內容
- 需要確保 Server Side 預先載入資料,生成完整的 HTML
解決方案: 使用 Nuxt 3 的 useFetch 或 useAsyncData
2.2 useFetch 基礎使用
檔案位置: pages/products/[id].vue
// 基本用法
const { data: product } = await useFetch(`/api/products/${route.params.id}`);
關鍵參數說明:
| 參數 | 說明 | 預設值 |
|---|---|---|
key | 唯一識別碼,用於 request deduplication | 自動生成 |
lazy | 是否延遲載入(不阻塞 SSR) | false |
server | 是否在 Server Side 執行 | true |
default | 預設值 | null |
transform | 資料轉換函式 | - |
2.3 完整實作範例
// pages/products/[id].vue
const { data: product } = await useFetch(`/api/products/${route.params.id}`, {
key: `product-${route.params.id}`, // 避免重複請求
lazy: false, // SSR 時等待完成
server: true, // 確保 server side 執行
default: () => ({
id: null,
name: '',
description: '',
image: '',
}),
transform: (data: any) => {
// 資料轉換邏輯
return {
...data,
formattedPrice: formatPrice(data.price),
};
},
});
關鍵點說明:
-
key參數- 用於 request deduplication(避免重複請求)
- 相同 key 的請求會被合併
- 建議使用唯一識別碼(如:
product-${id})
-
lazy: false- SSR 時會等待資料載入完成才渲染
- 確保搜尋引擎能看到完整內容
- 如果設為
true,SSR 時不會等待,可能導致內 容不完整
-
server: true- 確保在 Server Side 執行
- 這是 SSR 的關鍵設定
- 如果設為
false,只會在客戶端執行
2.4 useAsyncData vs useFetch
差異對比:
| 功能 | useFetch | useAsyncData |
|---|---|---|
| 用途 | 直接呼叫 API | 執行任意非同步操作 |
| 自動處理 | ✅ 自動處理 URL、headers | ❌ 需要手動處理 |
| 適用場景 | API 請求 | 資料庫查詢、檔案讀取等 |
使用範例:
// useFetch:適合 API 請求
const { data } = await useFetch('/api/products/123');
// useAsyncData:適合其他非同步操作
const { data } = await useAsyncData('products', async () => {
// 可以執行任何非同步操作
const result = await someAsyncOperation();
return result;
});
2.5 $fetch vs useFetch
面試常考題:什麼時候該用 $fetch,什麼時候該用 useFetch?
1. $fetch
- 定義:Nuxt 3 底層使用的 HTTP 客戶端(基於
ofetch)。 - 行為:單純發送 HTTP 請求,不會處理 SSR 狀態同步(Hydration)。
- 風險:如果在
setup()中直接使用$fetch,Server 端會請求一次,Client 端 Hydration 時會再次請求(Double Fetch),且可能導致 Hydration Mismatch。 - 適用場景:
- 使用者互動觸發的請求(如:點擊按鈕送出表單、載入更多)。
- Client-side only 的邏輯。
- Middleware 或 Server API route 內部。
2. useFetch
- 定義:包裝了
useAsyncData+$fetch的 Composable。 - 行為:
- 自動產生 key 進行 Request Deduplication。
- 處理 SSR 狀態傳輸(將 Server 取得的資料傳給 Client,避免 Client 再次請求)。
- 提供響應式回傳值(
data,pending,error,refresh)。
- 適用場景:
- 頁面初始化需要的資料(Page Load)。
- 依賴 URL 參數變動的資料取得。
總結比較:
| 特性 | useFetch | $fetch |
|---|---|---|
| SSR 狀態同步 | ✅ 有 (Hydration Friendly) | ❌ 無 (可能 Double Fetch) |
| 響應式 (Ref) | ✅ 回傳 Ref 物件 | ❌ 回傳 Promise (Raw Data) |
| 主要用途 | 頁面資料載入 (Data Fetching) | 事件處理、操作型請求 (Actions) |
// ⭕️ 正確:頁面載入使用 useFetch
const { data } = await useFetch('/api/user');
// ⭕️ 正確:點擊事件使用 $fetch
const submitForm = async () => {
await $fetch('/api/submit', { method: 'POST', body: form });
};
// ❌ 錯誤:在 setup 中直接使用 $fetch (導致 Double Fetch)
const data = await $fetch('/api/user');