資料獲取 Data Fetching
過去可能會使用 AXIOS
來打 api
,而 Nuxt3
提供了下列幾種打 api
的方法,這些方式只在 setup
或 生命週期內可以使用。
簡單說
使用 useFetch
會是最靈活的方法。
注意
nuxt3
啟動服務器在 localhost:3000
,若本機同時啟動「API 服務端」在 localhost:xxxx
供應用程式 請求 的話,可能會受到無法連線的影響。最好避開應用程式使用的服務。 (nuxt3
服務同時為 baseUrl
)
$fetch 基本請求
這是 nuxt3
請求 api 方法,可以直接在 nuxt
中直接 HTTP 請求資源。
$fetch( apiUrl
, options
)
vue
<template>
<div>
{{ data }}
</div>
</template>
<script setup>
const data = await $fetch('/api/v1/....')
</script>
提醒
$fetch
是一個promise
必須要await
非同步處理。- 在
server
與client
都會各執行一次,server
端的執行是為了提供「服務端渲染」,client
端在下載vue js
後也會再執行一次,不會寫入頁面原始碼。
🟡 服務端與客戶端資料不相符 Hydration text content mismatch in 🟡
初次頁面載入時 $fetch
會在「服務端」「客戶端」各執行一次,當兩個數據不符合時,log
會發出警告 Hydration text content mismatch in
。
解決方法
使用 useState
可以解決這個問題 參考
vue
<template>
<div>
{{ domText }}
</div>
</template>
<script setup>
const apiUrl = 'https://f48b-61-220-84-123.ngrok.io/user'
const res = reactive({ data: [] })
const data = await $fetch(apiUrl)
res.data = data
const domText = useState('user', () => res.data)
</script>
警示
這是 阻塞型 請求,在得到請求 回傳 後才會開始渲染頁面內容,在此之前會是「空白」頁面。
useAsyncData 非同步請求
在組件、頁面、或套件,都可以使用 useAsyncData
來進行「非同步」取得數據。
基本操作
- 頁面載入時僅會在
server 端
請求數據來渲染頁面。 <NuxtLink>
導航頁面,僅會在client 端
請求數據。
警示
這是 阻塞型 請求,在得到請求 回傳 後才會開始渲染頁面內容,在此之前會是「空白」頁面。
在 options
中加入 lazy: true
可解決這個部分。
useAsyncData( key
, handler
, options
)
參數:
key
唯一值密鑰 : 確保重新請求時,正確刪去之前請求舊的數據資料。handler
非同步執行函式 : 打 API 或加功數據資料的地方 (組合$fetch
使用)。options
選項 :lazy
懶加載 (默認false
):是否在路由加載後再 非同步請求,而不阻塞客戶端導航到頁面。
default
默認值 (工廠函式):回傳還沒執行 「非同步執行函式」 前的默認數據值,通常跟
lazy: true
搭配使用。(回傳應與請求獲得的數據格式相同)server
是否在服務端請求 (預設:true
):- 頁面載入會「服務端」請求數據。(若
server: false
僅會在「客戶端」請求數據,原始頁面為default
或空白)。 NuxtLink
導航會「客戶端」請求數據。
- 頁面載入會「服務端」請求數據。(若
transform
加工handler
回傳數據的函式:函式自帶的參數為 回傳的數據,可以加工後再
return
使用。pick
指定取得欄位資料 ([ resultKey ]
):handler
回傳資料的欄位,包含transform
的處理。watch
監聽響應式的資料 ([ref, reactive]
) 來自動打 api。當陣列內的「值」發生變化時,將會重新請求
handler
刷新資料。immediate
即時觸發 (預設true
)當
immediate: false
會避免 立即 觸發請求。(但事件觸發不影響)
Template
vue
<template>
<div>
{{ data }}
<button @click="refresh ++"> REFRESH </button>
<button @click="refreshBtn = !refreshBtn"> REFRESH (refreshBtn)</button>
</div>
</template>
vue
<script setup>
const apiUrl = 'https://f48b-61-220-84-123.ngrok.io/user'
const res = reactive({ data: [] })
const refresh = ref(1)
const refreshBtn = ref(true)
// useAsyncData options
const options = {
// 懶加載
lazy: true,
// 數據預設值
default: () => ({ id: '--', name: '--' }),
// 服務端請求
server: true,
// 加工回傳數據
transform: (value) => {
const cloneValue = { ...value }
cloneValue.transform = 'Ya'
return cloneValue
},
// 指定回傳資料欄位 (只取 'name' 的資料)
pick: ['name'],
// 監聽數據來重新請求
watch: [refresh, refreshBtn],
// 即時請求
immediate: false
}
const { data } = await useAsyncData('userList', () => $fetch(apiUrl), options)
res.data = data
</script>
提醒
若前往的路由,請求數據為 lazy: false
會阻止前往的導航,直到取得數據,為了用戶的使用體驗感受,建議使用 lazy: true
。
回傳值
data
請求回傳的數據,若回傳且有設置option.default
則會是預設值。pending
{boolean} 是否仍在獲取數據。refresh
{func} 重新執行請求數據函式 (handler
)。預設情況下
refresh
函式,必須執行完成才可以再次執行。error
{object} 請求錯誤信息,若無錯誤為null
。
js
const {
data,
pending,
refresh,
error
} = await useAsyncData('userList', () => $fetch(apiUrl))
注意
若不在服務端請求數據 option.server: false
,那 <script setup />
中的 data
為是為 null
。
useFetch 組合式請求
在頁面、組件、封裝邏輯都可以使用,是將 useAsyncData
與 $fetch
包裝在一個更簡單請求數據的函式,只需要 api URL
就可以使用了。 在 useFetch
中,會自動產生 key
來對應請求的數據更新。
useFetch(url
, options
)
js
const apiUrl = '/user'
const options = {
baseURL: 'https://ed91-61-220-84-123.ngrok.io'
method: 'post',
headers: { 'test-header': 'test' },
query: { query: 'test' },
params: {params: 'test'},
body: { id: 111, name: 999 },
default: () => ({ id: '--', name: '--' }), // 數據預設值
server: true, // 服務端請求
transform: (value) => { // 加工回傳數據
const cloneValue = { ...value }
cloneValue.transform = 'Ya'
return cloneValue
},
pick: ['name'],
watch: [refreshBtn],
// server: false,
// immediate: false
}
const { data, pending, refresh, error } = await useFetch(apiUrl, options)
參數
url
API 請求連結options
選項 (1/2):baseURL
{string} 基本請求 URL (與axios
相同)method
{string} 請求方法 (get
'、post
…)headers
{object} 請求頭部search
請求連結加上query
參數。params
與search
相同,擇一使用即可。body
{string / object} 若傳入物件
自動轉換成字串。有傳
body
的情況下,一定要設置方法 (ex:method: 'post'
) ,不然無法請求。
options
選項 (2/2): (與 useAsyncData 非同步請求 相同)key
唯一值密鑰: 確保重新請求時,正確刪去之前請求舊的數據資料 (供useAsyncData
使用)。lazy
懶加載 (默認false
):是否在路由加載後再 非同步請求,而不阻塞客戶端導航到頁面。
default
默認值 (工廠函式):回傳還沒執行 「非同步執行函式」 前的默認數據值,通常跟
lazy: true
搭配使用。(回傳應與請求獲得的數據格式相同)server
是否在服務端請求 (預設:true
):- 頁面載入會「服務端」請求數據。(若
server: false
僅會在「客戶端」請求數據,原始頁面為default
或空白)。 NuxtLink
導航會「客戶端」請求數據。
- 頁面載入會「服務端」請求數據。(若
transform
加工handler
回傳數據的函式:函式自帶的參數為 回傳的數據,可以加工後再
return
使用。pick
指定取得欄位資料 ([ resultKey ]
):handler
回傳資料的欄位,包含transform
的處理。watch
監聽響應式的資料 ([ref, reactive]
) 來自動打 api。當陣列內的「值」發生變化時,將會重新請求
handler
刷新資料。immediate
即時觸發 (預設true
)當
immediate: false
會避免 立即 觸發請求。(但事件觸發不影響)retry
重新請求 (預設undefined
),當onResponseError
時,會再重新請求一次。
回傳值
data
請求回傳的數據,若回傳且有設置option.default
則會是預設值。pending
{boolean} 是否仍在獲取數據。常見使用在是否顯示
loading
畫面v-if="pending"
。refresh
{func} 重新執行請求數據函式 (handler
)。預設情況下
refresh
函式,必須執行完成才可以再次執行。error
{object} 請求錯誤信息,若無錯誤為null
。
注意
若你提供 url 的方式是 function
、 ref
響應式 或 options
設置本身就是一個 function
,在使用 useFetch
時看起來就算是一樣,但數據還是不會匹配來做更新。如果是這樣的情況,必須要為 useFetch
設置上 key
來確保這種事情的發生。
攔截器 interceptors
useFetch
在請求方法中,options
也提供了幾個 hook
,可以針對 「請求前」、「獲得回傳」來處理特定事件: 參考 interceptors
onRequest
請求前處理: 一旦請求就會馬上執行。onRequestError
請求前發生錯誤執行onResponse
處理回傳數據,一旦得到回傳執行。在此處理回傳數據
response._data
,會造成onResponseError
無法正常運作。onResponseError
獲取回傳錯誤就算回傳錯誤
onResponse
這個hook
還是會觸發。
onResponseError 請求兩次
預設情況onResponseError
觸發後錯誤,會再執行一次 重新請求 重新跑一次,而在第二次 (最後一次) 請求的 options
內可以看到 retry: 0
,原因是預設 發生錯誤 會再重新請求一次,若不需要可以在請求加上 retry: 0
,這樣就算發生請求錯誤,也就只會請求一次。 參考
js
const apiUrl = '/user'
const options = {
baseURL: 'https://www.example.com',
onRequest({ request, options }) {
console.log(request) // /user <- 請求的 `url`
// 設置請求 headers
options.headers = { ...options.headers, Authorization: '111222333' }
// 請求 log
console.log(`[Fetch Request] /api${request}`, options, new Date())
},
onRequestError({ request, options, error }) {
console.log(error)
},
onResponse({ request, response, options }) {
console.log(request) // 請求的完整連結 request === response.url
// 回傳 log
console.log(`[Fetch Response] ${request}`, response._data, new Date())
return response._data
},
onResponseError({ request, response, options }) {
console.log('[------- fetch response error -------]', request, response.status, response._data)
},
}
const { data, pending, refresh } = await useFetch(apiUrl, options)
注意
當 onRequest
直接對 headers
內容賦值的話,onRequestError
會報錯。
js
onRequest({ request, options }) {
options.headers.Authorization = '...'
}
onRequestError
錯誤:
DOMException: Failed to execute 'fetch' on 'Window': The user aborted a request.
at $fetchRaw2
解決方式
整個 headers
重新賦值。
js
onRequest({ request, options }) {
options.headers = { ...options.headers, Authorization: '111222333' }
}
useLazyFetch 懶加載組合式請求
與 useFetch 組合式請求 功能一樣,就只是在 options
預設了 lazy: true
而已。這會讓路由導航不會因為等待請求的數據而 延後渲染畫面,相反的必須處理 數據為空 的情況,options.default
可以設置數據為空的默認值。
vue
<template>
<div v-if="pending">
Loading...
</div>
<div v-else>
{{ data }}
</div>
</template>
<script setup>
const apiUrl = '...'
const options = { ... }
const { data, pending } = await useLazyFetch(apiUrl, options)
</script>
提示
「參數」、「回傳值」與「攔截器」與 useFetch 組合式請求數據 相同。
注意
在這個方法下,就無法在 options
再設置 lazy: false
。
useLazyAsyncData 懶加載非同步請求
與 useAsyncData 非同步請求 功能一樣,就只是在 options
預設了 lazy: true
而已。這會讓路由導航不會因為等待請求的數據而 延後渲染畫面,相反的必須處理 數據為空 的情況,options.default
可以設置數據為空的默認值。
js
const { pending, data } = useLazyAsyncData('count', () => $fetch('/api/count'))
注意
在這個方法下,就無法在 options
再設置 lazy: false
。