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
去發起請求 場景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實戰系列之網路請求篇
聯絡作者:whitney1289(微信),iwhitney@163.com(郵箱)