Nuxt3提供了4種方式使得我們可以異步獲取數據
- useAsyncData
- useLazyAsyncData (useAsyncData+lazy:true)
- useFetch
- useLazyFetch (useFetch+lazy:true)
4種方式中,其實核心的就是useAsyncData和useFetch。這兩個方法不同於Nuxt2中的asyncData和fetch。接下來我們先來好好分析下這兩個方法。
useAsyncData
我們知道,在Nuxt2中,asyncData方法類似於一個生命週期函數,它在服務端或路由更新之前被調用。方法的參數是當前頁面的上下文對象,我們一般是利用 asyncData方法來獲取數據並返回給當前組件,以避免請求放在客户端執行時帶來的數據延遲出現問題。
export default {
data() {
return { project: 'default' }
},
asyncData(context) {
return { project: 'nuxt' }
}
}
在Nuxt3中,useAsyncData可以看做是異步獲取數據場景的一個封裝,而且變成了一個主動調用函數,原則上可以在任何時機調用。
// 用法
const {
data: Ref<DataT>,// 返回的數據結果
pending: Ref<boolean>,// 是否在請求狀態中
refresh: (force?: boolean) => Promise<void>,// 強制刷新數據
error?: any // 請求失敗返回的錯誤信息
} = useAsyncData(
key: string, // 唯一鍵,確保相同的請求數據的獲取和去重
fn: () => Object,// 一個返回數值的異步函數
options?: { lazy: boolean, server: boolean }
// options.lazy,是否在加載路由後才請求該異步方法,默認為false
// options.server,是否在服務端請求數據,默認為true
// options.default,異步請求前設置數據data默認值的工廠函數(對lazy:true選項特別有用)
// options.transform,更改fn返回結果的函數
// options.pick,只從數組中指定的key進行緩存
)
從api的設計中可以看出,useAsyncData沒有限制我們發起網絡請求的方式,同時它還暴露了請求狀態,增加了刷新控制,以及對重複獲取數據的去重控制等。
使用示例如下:
<script setup>
const { data } = await useAsyncData('count', () => $fetch('/api/count'))
</script>
<template>
Page visits: {{ data }}
</template>
useFetch
在Nuxt2中,fetch 方法用於在渲染頁面前填充應用的狀態樹(store)數據, 與 asyncData 方法類似,不同的是它不會設置組件的數據。
<template>
<h1>Stars: {{ $store.state.stars }}</h1>
</template>
<script>
export default {
fetch({ store, params }) {
return axios.get('http://my-api/stars').then(res => {
store.commit('setStars', res.data)
})
}
}
</script>
在Nuxt3中,useFetch實際上是對useAsyncData和$fetch的封裝,提供了一個更便捷的封裝方法。它相比useAsyncData, 主要做了以下兩點處理:
- 它會根據URL和fetch參數自動生成一個key,同時推斷出API的響應類型。也就是説不用手動指定key了。
-
它實現了網絡請求的具體方式,使用
$fetch發起請求,也就是説不需要再手動去實現網絡請求的邏輯了。//useFetch用法 const { data: Ref<DataT>, pending: Ref<boolean>, refresh: (force?: boolean) => Promise<void>, error?: any } = useFetch(url: string, options?) // options (繼承自 unjs/ofetch options以及 AsyncDataOptions) // 下邊的這些參數是useAsyncData的options中沒有的 // options.method: 請求方式 // options.query: url路徑參數 // options.params: query參數的別名 // options.body: 請求體參數, // options.headers: 請求頭的配置 // options.baseURL: 請求的基礎Url地址實戰應用
我們不難發現,
useFetch已經具備了網絡請求的所有核心功能,雖然該Api主要用於在服務端請求,但它也是做了客户端請求的支持的,只要稍加封裝改動,就可以同時用於服務端請求和客户端請求的場景。這樣我們也就不用額外再引入像Axios這樣的請求庫了。場景1: 如何處理對於帶錯誤碼的數據響應
通常我們的接口都不是直接返回數據,而是帶了一個錯誤碼和錯誤信息的對象,比如這樣:
// response: { data: {age: 1}, code: 1 }在這樣的返回結構下,
useFetch拿到的數據並不是我們真實想要的數據const { data } = await useFetch('/api/user/info', { method: 'get' }) console.log(data) // 此時data是一個Ref包裹的對象{ data: {age: 1}, code: 1 } const userInfo = unref(data).data //獲取真正的數據需要先unref後再去獲取data所以,我們希望能在接口返回時對數據做一下轉換,這裏其實
useFetch提供了相關的option參數,我們可以這樣修改const { data: userInfo} = await useFetch('/api/user/info', { method: 'get', // 處理方式1 onResponse({ response }) { response._data = { ...response._data.data, } }, // 處理方式2 // transform: (res) => { // return res.data // }, })場景2: 如何只在客户端側發起請求
這樣的場景一般用於使用靜態化構建部署,但是頁面上有些內容是不能在構建時靜態化的。這時可以利用
server:false參數// 異步獲取當前用户信息 const { data: userinfo } = await useMyFetch('/api/auth/userinfo', { server: false })注意: 這種情況下,如果想在script內直接獲取到userinfo的內部值,是獲取不到的!官方文檔也做了對應的説明:
if you have not fetched data on the server (for example, with
server: false), then the data will not be fetched until hydration completes. This means even if you await useFetch on client-side, data will remain null within<script setup>.
如果非要在script中獲取數據呢?這裏筆者想到兩個方案:
- 用
$fetch去發起請求 -
用
watch監聽userinfo的值變化場景3: 如何將請求結果轉為非響應式的數據
這種場景一般用於在客户端發起的請求,我們不需要在頁面上渲染響應的數據,只是為了做一些邏輯判斷或者需要對數據進行加工。而
useFetch請求後的返回值默認都是一個ref對象,我們得先獲取內部值。const { data } = await useMyFetch('/api/get-actiocn-token') // data是一個Ref包裹的對象,需要用unref獲取內部值 const tokenInfo = unref(data)如果想直接獲取原始數據的話,
useFetch原生是不支持的(或者是我現在還不知道怎麼實現)。我們只能使用$fetch去實現了。
請求統一封裝
針對上述三種場景,筆者分享下自己的封裝思路,即在composables目錄中實現一個useMyFetch方法,去處理一些通用的邏輯
import type { NitroFetchRequest } from 'nitropack'
import type { FetchOptions, FetchResponse } from 'ofetch'
import type { UseFetchOptions } from 'nuxt/dist/app/composables/fetch'
function transFormResponse({ response }: any) {
// 處理後端攜帶了錯誤碼響應的數據
if (response._data && response._data.code)
return Promise.reject(response._data)
response._data = {
...response._data.data,
}
}
/**
* 封裝$fetch用於簡單請求場景
* @param request
* @param opts
* @returns
*/
export function useClientFetch(request: NitroFetchRequest, opts?: FetchOptions<any>) {
return $fetch<FetchResponse<any>>(request, {
onResponse: transFormResponse,
...opts,
})
}
/**
* 抽離useFetch的通用配置
* @param request
* @param opts
* @returns
*/
export function useMyFetch(request: NitroFetchRequest, opts?: UseFetchOptions<any>) {
return useFetch(request, {
onResponse: transFormResponse,
...opts,
})
}
/**
* 實現更便捷的post請求
* @param request
* @param opts
* @returns
*/
useMyFetch.get = (request: NitroFetchRequest, opts?: UseFetchOptions<any>) => {
return useMyFetch(request, {
method: 'get',
...opts,
})
}
/**
* 實現更便捷的post請求
* @param request
* @param opts
* @returns
*/
useMyFetch.post = (request: NitroFetchRequest, opts?: UseFetchOptions<any>) => {
return useMyFetch(request, {
method: 'post',
...opts,
})
}
結語
博客原創地址:Nuxt3實戰系列之網絡請求篇
聯繫作者:imwty2023(微信),iwhitney@163.com(郵箱)