vue3 封装网络请求
前言
怎么说呢,好久没更新博客了。正好不是很想做别的事情,于是简单写一下最近的一些收获吧。
在做学院集市的时候我封装过一次网络请求,不过并没有使用组合式函数,大概是这样的
js
async function requestFunc(url, object, tokenIsNeeded) {
if (tokenIsNeeded) {
const token = getTokenWithExpiry('token');
if (!token) {
showMsg('登录过期,请重新登录');
window.location.reload();
return null;
}
const res = await fetch(`${apiUrl}${url}`, {
method: object.method,
headers: {
...object.headers,
Authorization: `Bearer ${token}`,
},
body: JSON.stringify(object.body),
});
return res;
} else {
const res = await fetch(`${apiUrl}${url}`, {
method: object.method,
headers: object.headers,
body: JSON.stringify(object.body),
});
return res;
}
}
export { requestFunc };
使用这个函数来发送网络请求,其它的是这样的
js
async function updateUserInfo(avatarURL, intro, name, userID) {
try {
const res = await requestFunc(
`/auth/updateUserInfo`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
avatarURL: avatarURL,
intro: intro,
name: name,
userID: userID,
},
},
true
);
const data = await res.json();
return data;
} catch (e) {
alert(e);
console.error(e);
}
}
怎么说呢,只能说还好当时后端返回的格式不统一吧,有的直接返回数据,有的还要访问一下 data,有的啥都不返回。这样写也没啥问题。不过这次的 java 大作业的返回格式很统一
json
{
"code": 200,
"message": "success",
"data": "data"
}
实际数据都在 data 里,这时候如果还这么写就有些不妥。会发现每次用 requestFunc 都要访问 data 才能拿到实际数据。
使用组合式函数封装网络请求
这里简单地返回 data 而不是 res 也没啥问题,不过当时想试着用组合式函数来做,干脆重新写了。
当时接触了骨架屏这个东西,我希望网络请求还没完成的时候就展示骨架屏,所以我需要一个变量来反映网络是否完成;除了这个,我还需要一个变量来反映有没有错误,所以我的函数应该返回
ts
export interface RequestResult<T> {
data: Ref<T | undefined>; // 实际数据
isLoading: Ref<boolean>;
err: Ref<string>;
}
useRequest 也知道大概要怎么写了
ts
import { ref } from 'vue'
export interface ReturnData<T> {
code: number;
message: string;
data: T;
}
export async function useRequest<T>(
url: string,
requestInit: RequestInit = {
method: 'GET',
headers:{
Content-Type:'application/json'
}
},
tokenIsNeeded: boolean = true
) {
const data:Ref<T|undefined> = ref<T>();
const isLoading = ref(true)
const err = ref('')
if(tokenIsNeeded){
const token = localStorage.getItem('token');
const headers = new Headers(requestInit.headers ?? {});
headers.set('Authorization', `Bearer ${token}`);
requestInit.headers = headers;
}
try{
const res = await fetch(`${apiUrl}${url}`,requestInit)
if(!res.ok){
throw new Error(`HTTP error! status: ${response.status}`);
}
const jsonData: ReturnData<T> = await res.json()
if(jsonData.code === 1){
data.value = jsonData.data;
isLoading.value = false;
}else {
throw new Error(jsonData.message);
}
}catch(e){
if (e instanceof Error) {
err.value = e.message;
} else {
err.value = '未知错误';
}
isLoading.value = false;
}
return {
data,
isLoading,
err
}
}
这样的话封装其它请求只需要这样
ts
import { useRequest } from '../request';
import type { UserInfo } from '@/model/dto/UserApi/UserInfo';
export function GetAllUser() {
return useRequest<UserInfo>('/users');
}
在组件使用
vue
<script setup lang="ts">
import { GetAllUser } from '@/xxx/xxx'
const { data, isLoading, err } = await GetAllUser()
</script>
<template>
<div>
<template v-if="!isLoading">
<UserCard v-for="user in data" />
</template>
<template v-else>
<skeleton v-for="index in 9" />
</template>
</div>
</template>
这样会有一点点问题,控制台会报错说要用Suspense什么的,改成用then就可以解决报错。
不过我去翻vueuse的useFetch文档的时候,里面写只要用了useFetch,不管有没有await都需要套在Suspense里面,我也不太清楚原因,但是我这样好像又没有问题,问了ai也不太明白。