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,
          intro,
          name,
          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也不太明白。