在项目当中大量使用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
npm i howuse
安装axiosnpm 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);
}
)
// 这里定义了后端常用的接口形式
// 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);
这里的参数和返回值咋一看挺复杂,不过使用多了,爱不释手。继续往下看
我这里举一些例子:
api 文件
// 根据ColumnId 查询列表,主要用于栏目排序。这里有一个参数在路径上
export function useArticleListByColumnIdRequset() {
return useDefAxiosRequest({ url: "/article/admin-list/{columnId}" })
// 如果是POST
// return useDefAxiosRequest({ 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值,可以查询之后 直接绑定到页面上了。
config
类型 HowAxiosRequestConfig
在 AxiosRequestConfig 基础上扩展,完全兼容。和axios完全保持一直,增加path
参数,参阅官网
https://axios-http.com/docs/req_config
options
类型 HowExRequestOptions
参数 | 类型 | 描述 |
---|---|---|
immediate | boolean | 默认 false, 是否在页面加载时,立即执行 |
delay | number | 默认 500, 接口防抖延迟时间 |
isDebounce | boolean | 默认 true, 是否开启接口防抖 |
defaultVal | any | 默认 undefined, 接口返回时的默认值 |
响应
response,响应实体
data, 响应实体数据
error,错误信息
edata,错误信息实体数据
execute, 执行调用接口
aborted, 接口是否被取消
abort, 取消接口
finished, 接口请求是否完成
loading,接口是否正在加载中…