前言
之前写过一篇关于axios封装的文章(axios封装—vue3项目),最近又有一点新的收获,关于loading提示的控制以及取消请求的逻辑,所以赶紧写写文章总结记录下来,顺便分享给大家。
一、Loading白名单
关于接口请求Loading的部分,贴一下之前写的部分代码:
// loading 次数
let loadingCount = 0;
service.interceptors.request.use(
config => {
// 加入Loading
showLoadingToast({
message: '加载中...',
//禁止背景点击
forbidClick: true,
});
loadingCount++;
return config;
},
error => {
return Promise.reject(error);
}
);
由于之前是在axios的请求拦截器里加入Loading的,这样相当于给所有接口都加上了,不够灵活,所以需要改进一下,给某些接口一个escape hatch(安全舱口):
// 定义白名单,里面的接口请求不会有Loading图标
const whiteList = ['getList'];
// 请求拦截器
service.interceptors.request.use(
config => {
// 加入Loading,白名单控制;
let url = config.url;
let index = url.lastIndexOf('/');
let endIndex = url.lastIndexOf('?') === -1 ? url.length : url.lastIndexOf('?');
// let path = url.substring(index + 1, url.length);
// if (path.indexOf(whiteList) === -1) {
let path = url.substring(index + 1, endIndex);
if (!whiteList.includes(path)) {
if (loadingCount === 0) {
// 加入Loading;
showLoadingToast({
message: '加载中...',
forbidClick: true,
});
}
loadingCount++;
}
return config;
},
error => {
return Promise.reject(error);
}
);
// 响应拦截器
service.interceptors.response.use(
response => {
// 关闭loading,白名单控制
let url = response.config.url;
let index = url.lastIndexOf('/');
let endIndex = url.lastIndexOf('?') === -1 ? url.length : url.lastIndexOf('?');
// let path = url.substring(index + 1, url.length);
// if (path.indexOf(whiteList) === -1) {
let path = url.substring(index + 1, endIndex);
if (!whiteList.includes(path)) {
loadingCount--;
}
if (loadingCount === 0) {
closeToast();
}
return response;
},
代码很简单,就是对url请求地址与whiteList白名单中地址进行匹配,我这里直接拿接口最后‘/’与’?'之间的接口名称进行includes方法的一个匹配。这个匹配规则还是得看自己项目实际,要是有两个不同接口后半段一样的话就得改一下了,总之验证测试少不了的。
二、取消请求
为什么要取消请求呢,其实这种场景还真不少,比如搜索输入框的提示词,每当你输入内容改变的时候,就会去调用接口,返回当前输入内容的关联字,但是第一次请求网络卡了第二次网络又正常了,导致第二次响应的接口数据回来了之后第一次的才回来,页面提示词先是第二次的内容,瞬间又变成第一次的内容,这就造成了一个提示词不对的问题。
当然解决这种问题有多种方案,可以使用防抖节流控制,也可以使用取消请求的方法,如果是切换标签展示内容的情况还可以使用加Loading提示阻止切换,如果是单个请求太长还可以为此设置接口的timeout。我们得根据项目实际需求灵活使用或者组合使用,根据场景找到最合适的方案。接下来回归主题,在Axios封装中加入取消请求的逻辑,取消请求好处在于可以避免资源占用,以及避免接口回调函数里一些节点已经销毁了还要继续运行的异常场景。
1. 自动处理
// 中断请求逻辑
const CancelToken = axios.CancelToken;
let pending = {
};
function removePending(key, isRequest = false) {
if (pending[key] && isRequest) {
pending[key]('中断请求');
}
delete pending[key];
}
// 请求拦截器
service.interceptors.request.use(
config => {
// 取消请求,根据url及请求方法定位到该请求
const key = config.url + '&' + config.method;
// 若此请求已存在于pending列表中说明是重复请求,则将其中断掉
removePending(key, true);
config.cancelToken = new CancelToken(c => {
pending[key] = c;
});
return config;
},
error => {
return Promise.reject(error);
}
);
// 3.响应拦截器
service.interceptors.response.use(
response => {
const key = response.config.url + '&' + response.config.method;
// 接口请求正常返回则将pending列表中该请求记录去掉
removePending(key);
return response;
},
error => {
if (error.name === 'CanceledError') {
// 此种情况则是请求被取消,可对此进行处理
return Promise.reject(error.message);
} else {
// 超时处理
error.message = '服务器响应超时,请刷新当前页';
}
showToast(error.message);
return Promise.reject(error.response);
}
);
所谓自动处理,就是可以对页面上所有内容的请求都走这么一个处理逻辑,如果出现有两个相同api同时请求则利用axios的CancelToken
方法将旧的请求取消。
那么有了自动处理就够了吗?答案是得看场景。比如还是搜索的输入框,输入框有可以让用户手动清空输入框的按钮/图标,下面展示的是搜索结果,想象一下如果用户先输入了一些文本,这时候发起网络请求但是请求还没响应,用户点清空输入,紧接着接口结果又回来了,下面内容区就把搜索结果展示出来了,可是用户已经不关心上一个的结果了,这样就显得很怪异用户体验不好。这种情况下就必须得在用户点清空的时候手动调用中断请求函数,避免还在pending的接口在不适当的时机将内容展现出来。
2. 手动操作
// request.js
const Request = (url, options = {
}) => {
let method = options.method || 'get';
let params = options.params || {
};
// 在参数内加入cancel_http字段(字段名避免与其他传参混淆即可)表示取消该请求接口
if (options.params?.cancel_http) {
const key = url + '&' + method;
if (pending[key]) {
pending[key]('中断请求');
}
return;
})
// ...
};
2.1 调用方式
<script setup>
import {
getList } from "./api/index";
// 正常调用时
let params = {
pageNum: 1,
pageSize: 10,
};
getList(params).then(res => {
// ...
});
// 手动取消该请求,只需要传入cancel_http参数true即可,不关心其他参数
getList({
cancel_http: true,
});
</script>
2.2 错误处理
由于针对这种取消请求的情况返回了Promise.reject方法(如果前面不是这么处理返回的话就不需要这个步骤了),所以当请求被取消后会抛出错误,可以使用.catch
方法捕获错误,如:
getList(params)
.then(res => {
// ...
})
.catch(err=>{
// ...
});
也可以使用try-catch
方法捕获:
const click = async () => {
try {
const res = await getList(params);
// ...
} catch (error) {
// ...
}
};
总结
此次有关axios封装的分享到此结束,本文的Loading提示算是对之前文章的一个优化吧,取消请求这部分就是为了避免bug的产生,可以根据项目需求结合使用,总的来说前端的优化最终还是为了用户体验。
如果此篇文章对您有帮助,欢迎您【点赞】、【收藏】!也欢迎您【评论】留下宝贵意见,共同探讨一起学习~