資料獲取
Nuxt 4 提供了強大的資料獲取組合式函數 (Composables),完美解決了 SSR 環境下的重複請求 (Double Fetching) 與水合不一致 (Hydration Mismatch) 問題。
為什麼不能直接用 fetch?
在傳統的 Vue SPA 中,我們習慣在 onMounted 鉤子中呼叫 API。 但在 Nuxt 的 SSR (伺服器端渲染) 模式下,這種做法會帶來兩個嚴重問題:
Traditional Fetch
CLIENT-SIDE PATTERN
Nuxt useFetch
UNIVERSAL DATA FETCHING
Under the Hood
SSR Data Pipeline1. Prefetch
Server 執行請求並等待結果
2. Serialize
資料嵌入 HTML <script>
3. Hydrate
前端直接還原狀態,零請求
基本用法
useFetch 是 Nuxt 提供的全能型工具,它自動處理了上述所有問題。你只需要一行程式碼,就能獲得響應式的資料與請求狀態。
const { data, status, error, refresh } = await useFetch('/api/users')
// data: 回傳的資料 (Ref<T>)
// status: 請求狀態 'idle' | 'pending' | 'success' | 'error'
// error: 錯誤物件
// refresh: 重新執行請求的函式Ref<T>。若請求失敗則為 null。'idle' | 'pending' | 'success' | 'error'
進階特性
Lazy Loading (非阻塞導航)
預設情況下,Nuxt 會等待所有 useFetch 完成後才切換頁面 (Blocking Navigation)。
若 API 回應較慢,使用者會感覺點擊後「沒反應」。啟用 lazy: true 後,頁面會立即切換,並在背景載入資料。這時你需要透過 status 來顯示 Loading 骨架屏。
<script setup>
// 啟用 lazy: true,不會阻塞頁面導航
const { data, status } = await useFetch('/api/posts', {
lazy: true
})
</script>
<template>
<!-- 透過 status 處理載入狀態 -->
<div v-if="status === 'pending'">
載入中...
</div>
<div v-else>
{{ data }}
</div>
</template>自動監聽與參數更新
在製作搜尋、分頁功能時,我們希望當參數改變時自動重抓資料。 useFetch 會自動解包 (Unwrap) 傳入 query 的 ref。或者,你也可以使用 watch 選項來明確指定要監聽的變數。
const page = ref(1)
const search = ref('')
const { data } = await useFetch('/api/users', {
// query 參數會自動解包 ref
query: {
page,
q: search
},
// 當 page 或 search 改變時,自動重新發送請求
watch: [page, search]
}) useAsyncData 的使用時機
useFetch 其實是 useAsyncData 加上 $fetch 的語法糖。 但在某些情況下,你必須直接使用 useAsyncData:
- 整合第三方 SDK (Firebase, CMS Clients, GraphQL)。
- 需要串聯多個請求後才回傳單一結果。
// 當你需要執行非 fetch 的異步操作 (例如 CMS SDK 或 GraphQL)
const { data } = await useAsyncData(
'unique-key-for-caching', // [重要] 必須提供唯一的 key
async () => {
const cmsData = await myCmsClient.getPosts()
return cmsData.filter(post => post.published)
}
)useAsyncData 時,務必提供一個唯一的 key。Nuxt 依賴這個 key 在 Server 與 Client 之間傳遞資料快取 (Payload)。若 key 重複,可能導致資料汙染或水合錯誤。 $fetch vs useFetch
Nuxt 提供了兩個長得很像的工具,初學者常混淆。請記住以下原則:
useFetch
Composable (Setup Context)
$fetch
Utility Function (Anywhere)
實戰演練
以下範例展示了如何從 JSONPlaceholder API 獲取使用者列表。我們使用了 lazy 模式來優化載入體驗,並透過 transform 只保留前 4 筆資料。
使用者列表
type User = { id: number; name: string; email: string }
// [實戰範例] 結合 Lazy Loading 與資料轉換
const { data: users, status, error, refresh } = await useFetch<User[]>(
'https://jsonplaceholder.typicode.com/users',
{
lazy: true, // 非阻塞模式
// [重點] 只取前 4 筆,減少前端處理負擔
transform: (users) => users.slice(0, 4),
// [重點] 設定唯一的 key,避免水合不一致
key: 'users-list-demo'
}
)status === 'pending' 的實際應用。