在项目当中大量使用axios,我一般会创建一个request.js文件。接着在项目src文件夹下面创建api文件夹。大致的代码如下。

// 封装
const service = axios.create({
  baseURL: '/',
  headers: {
  }
})

// api 文件定义
export const xxxType = (params: any) =>
    service({
        url: "/xxx/types",
        method: "get",
        params,
    });

// 使用
xxxType({ dimension }).then(({data})=>{
      tableLists.value = data?.data || []
})

上面三层架构在项目当中屡试不爽,尤其在大型项目当中,维护起来不要太方便。但是事情远远不是想想的那么简单,有一天测试给我说,你这代码有个bug,这个按钮如果迅速点击,会不停的掉接口。啊?能用就行了,你管这干啥。 事情不光如此简单,有个页面有几个数据巨多的接口,本来后端反应就慢,页面反应也慢,当不停的切换页面的时候,接口疯狂跑向后台,一会儿服务端产生大量的计算。后端同学说:能不能在页面离开的时候中断这个请求呢?long long ago, 前后端不分离的时候也没见酱紫呀。

要命的是,目前项目开始使用vue3框架,这个框架setup的使用方法很好用,可以减少不少的代码。别人都各种use简化代码的时候,我这里还沉沦在Promise的回调地狱当中。

直到遇见howuse/axios,作者的文档少啊,让我走了不少坑。这里认真总结下。

开始入坑 howuse/axios

简介

安装howuse npm i howuse 安装axios npm i axios github 地址:https://github.com/biancangming/howuse

相对于原生axios解决问题:

1. 加入节流自动过滤多余的重复请求,节省带宽同时,一定程度防止表单短时间重复提交的问题

2. vue页面销毁自动取消未完成的请求,节省页面资源

3. 在有必要的情况下,可手动终止当前请求

4. 响应式用法

5. 响应默认值设置

6. 增加path传参模式

先创建个实例,拦截器

import { createAxios, type HowAxiosRequestConfig, type HowExRequestOptions } from "howuse/axios"
import { AUTHORIZATION } from './const';

// createAxios 相当于 axios.create,参数完全保持一直, server 相当于 axios.create 的返回值
// 这里很明显,作者没有进行多余的装饰,只是额外封装了 useAxiosRequest 和 useBlobDownload 两个方法
export const { useAxiosRequest, useBlobDownload, server } = createAxios({
  baseURL: "/",
  timeout: 0
});

// 拦截器的创建和原生一模一样
// 请求拦截器
server.interceptors.request.use(
  config => {
    const token = sessionStorage.getItem(AUTHORIZATION)
    if (!token) {
      console.warn("token获取失败...")
    }

    // 设置请求头
    if (config.headers) {
      config.headers[AUTHORIZATION] = `Bearer ${token}`
    }
    return config;
  },
  (error) => {
    return Promise.reject(error);
  });

// 响应拦截器 适合做一些不影响数据结构的操作,例如token失效返回登录页面等
server.interceptors.response.use(
  (response) => {
    if(response.data?.code === 1 && response.data?.msg) {
      ElMessage.error(response.data.msg);
    }
    // return response.data
    // 网上许多示例将此处直接设置为 response.data ,一定程度上影响未来响应头的获取,一旦需要使用响应头时,变得异常困难
    // 正确的方式应该是将响应的server二次封装
    return response
  },
  (error) => {
    const status = error?.response?.status
    if (status === 401) {
      sessionStorage.clear()
      localStorage.clear()
      // 跳转到登录页面 ...
    } else {
      ElMessage.error(error.response.data.msg || httpStatus[error.response.status]);
    }
    return Promise.reject(error);
  }
)

Api封装

// 这里定义了后端常用的接口形式
// T 根据自己的业务传入
export interface DefResult<T> {
  msg: string,
  data: T,
  code: 0 | 1
}

// 假如我的数据如下
interface IData {
  title: string,
  value: string,
}

// 我在页面使用如下
const { response, data, error, edata,  execute, aborted, abort, finished, loading } = useAxiosRequest<DefResult<IData>>(config, options);

这里的参数和返回值咋一看挺复杂,不过使用多了,爱不释手。继续往下看

我这里举一些例子:

一个简单的GET 请求

api 文件

// 根据ColumnId 查询列表,主要用于栏目排序。这里有一个参数在路径上
export function useArticleListByColumnIdRequset() {
  return useDefAxiosRequest<IArticle[]>({ url: "/article/admin-list/{columnId}" })
  // 如果是POST
  // return useDefAxiosRequest<IArticle[]>({ url: "/article/admin-list/{columnId}", method: "POST" })
}

页面当中

const { execute: articleListByColumnIdexecute, data: articleListByColumnIData , abort } = useArticleListByColumnIdRequset()

articleListByColumnIdexecute({ path: { columnId } }) // 路径参数,当然也可以使用 params 和 data

// 假如需要对返回数据二次加工可以直接这样子
const dataSource = computed(() => unref(articleListByColumnIData).data || [])

// 如果想要在请求完成后做一些事情,可以直接.then
articleListByColumnIdexecute({ path: { columnId } }).then(res=>{
   responseMsg(res)
})

// 页面离开中断请求
onUnmounted(() => {
    abort()
})

这里的 articleListByColumnIData 返回是一个ref值,可以查询之后 直接绑定到页面上了。

useAxiosRequest 参数说明

  1. config 类型 HowAxiosRequestConfig 在 AxiosRequestConfig 基础上扩展,完全兼容。

和axios完全保持一直,增加path参数,参阅官网 https://axios-http.com/docs/req_config

  1. options 类型 HowExRequestOptions

| 参数 | 类型 | 描述 | | - | - | - | | immediate | boolean | 默认 false, 是否在页面加载时,立即执行 | | delay | number | 默认 500, 接口防抖延迟时间 | | isDebounce | boolean | 默认 true, 是否开启接口防抖 | | defaultVal | any | 默认 undefined, 接口返回时的默认值 |

  1. 响应

  2. response,响应实体

  3. data, 响应实体数据

  4. error,错误信息

  5. edata,错误信息实体数据

  6. execute, 执行调用接口

  7. aborted, 接口是否被取消

  8. abort, 取消接口

  9. finished, 接口请求是否完成

  10. loading,接口是否正在加载中...