vue3+ts Axios封装—重复请求拦截
创建好vue3项目
1.安装Axios与Element Plus
Axios安装
npm install axios
Element Plus 安装
官网入口:https://element-plus.gitee.io/zh-CN/
npm install element-plus --save
Element 主要用到信息提示 与 全屏加载动画
2.在src 目录下创建 api 文件夹和 utils 文件夹
api 文件夹下 封装 Axios封装 与 请求配置
utils 文件夹下 operate.ts 配置接口地址 与其他全局ts
3.Axios封装
旧版本地址:https://www.cnblogs.com/lovejielive/p/16363587.html
新版本:主要增加动态控制是否显示加载动画。
是否需要判断重复请求。
优化请求接口配置参数写法。
扩展AxiosRequestConfig 增加自定义参数
declare module 'axios' {
//请求自定义参数
interface AxiosRequestConfig {
// 是否显示加载框
ifLoading?: boolean
// 是否允许重复请求
repeatRequest?: boolean
// 登录 token
isToken?: any;
}
}
3.1重复请求判断
通过配置repeatRequest是否允许重复请求,来开启判断。主要在api.ts中配置。
每一次请求创建一个key,判断是否存在,如存在执行.abort()取消当前请求,
不存在pendingMap中新增一个key。
通过AbortController来进行手动取消。
主要代码
//格式化请求链接
function getRequestKey(config: AxiosRequestConfig) {
const { url, method, data, params } = config,
//字符串化参数
dataStr = JSON.stringify(data) || '',
paramsStr = JSON.stringify(params) || '',
//记得这里一定要处理 每次请求都掉会变化的参数(比如每个请求都携带了时间戳),否则二个请求的key不一样
key = [method, url, dataStr, paramsStr].join("&");
return key;
} //创建存储 key 的 集合
const pendingMap = new Map() //是否重复请求key
function setPendingMap(config: AxiosRequestConfig) {
//手动取消
const controller = new AbortController()
config.signal = controller.signal
const key = getRequestKey(config)
//判断是否存在key 存在取消请求 不存在添加
if (pendingMap.has(key)) {
// abort取消请求
pendingMap.get(key).abort()
//删除key
pendingMap.delete(key)
} else {
pendingMap.set(key, controller)
}
}
在接口消息提示时,通过 axios.isCancel(error) 过滤掉已取消的请求,
//拦截掉重复请求的错误,中断promise执行
if (axios.isCancel(error)) return []
3.2 axios完整代码
api文件夹下 创建 request-wrapper.ts Axios封装
/*
* @description: 请求封装
* @Author: Jay
* @Date: 2023-04-11 13:24:41
* @LastEditors: Jay
* @LastEditTime: 2023-09-04 11:49:10
*/ // 导入axios
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
// 使用element-ui ElMessage做消息提醒 ElLoading加载
import { ElMessage, ElLoading } from "element-plus";
//请求头
import operate from "@/utils/operate" //加载配置
let loadingInstance: { close: () => void };
let requestNum = 0;
//加载动画
const addLoading = () => {
// 防止重复弹出
requestNum++;
if (requestNum === 1) {
loadingInstance = ElLoading.service({ fullscreen: true });
}
}
// 关闭 加载动画
const cancelLoading = () => {
requestNum--;
if (requestNum === 0) loadingInstance?.close();
} //格式化请求链接
function getRequestKey(config: AxiosRequestConfig) {
const { url, method, data, params } = config,
//字符串化参数
dataStr = JSON.stringify(data) || '',
paramsStr = JSON.stringify(params) || '',
//记得这里一定要处理 每次请求都掉会变化的参数(比如每个请求都携带了时间戳),否则二个请求的key不一样
key = [method, url, dataStr, paramsStr].join("&");
return key;
} //创建存储 key 的 集合
const pendingMap = new Map() //是否重复请求key
function setPendingMap(config: AxiosRequestConfig) {
//手动取消
const controller = new AbortController()
config.signal = controller.signal
const key = getRequestKey(config)
//判断是否存在key 存在取消请求 不存在添加
if (pendingMap.has(key)) {
// abort取消请求
pendingMap.get(key).abort()
//删除key
pendingMap.delete(key)
} else {
pendingMap.set(key, controller)
}
} //增加新的请求参数类型
declare module 'axios' {
//请求自定义参数
interface AxiosRequestConfig {
// 是否显示加载框
ifLoading?: boolean
// 是否允许重复请求
repeatRequest?: boolean
// 登录 token
isToken?: any;
} // 解決 类型“AxiosResponse<any, any>”上不存在属性“code”
// interface AxiosResponse<T = any> {
// // 请求 data 里的一级参数
// code: number;
// time: string;
// msg: string;
// data: T;
// }
} //创建axios的一个实例
const axiosInstance: AxiosInstance = axios.create({
//接口统一域名
baseURL: operate.baseUrl(),
//设置超时
timeout: 1000 * 30,
//跨域携带cookie
withCredentials: true,
}) // 添加请求拦截器
axiosInstance.interceptors.request.use(
(config) => {
//加载动画
if (config?.ifLoading) addLoading();
//是否判断重复请求
if (!config.repeatRequest) {
setPendingMap(config)
} //判断是否有token 根据自己的需求判断
const token = config.isToken
console.log("判断是否有token", token)
if (token != undefined) {
//如果要求携带在参数中
config.params = Object.assign({}, config.params, token)
// 如果要求携带在请求头中
// config.headers = Object.assign({}, config.headers, operate.uploadParameters())
}
return config
},
(error: AxiosError) => {
return Promise.reject(error)
}
) // 添加响应拦截器
axiosInstance.interceptors.response.use((response: AxiosResponse) => {
const config = response.config
// 关闭加载 动画
if (config?.ifLoading) cancelLoading();
//是否登录过期
if (response.data.code == 400 || response.data.code == 401) {
ElMessage.error("登录过期,请重新登录")
// //清除登录缓存
// store.commit("LOGOUT")
// //返回首页
// setTimeout(() => {
// router.push("/");
// }, 500);
return
}
// 返回参数
return response.data
}) // 错误处理
axiosInstance.interceptors.response.use(undefined, (error) => {
const config = error.config
// 关闭加载 动画
if (config?.ifLoading) cancelLoading(); //拦截掉重复请求的错误,中断promise执行
if (axios.isCancel(error)) return [] /***** 接收到异常响应的处理开始 *****/
if (error && error.response) {
// 1.公共错误处理
// 2.根据响应码具体处理
switch (error.response.status) {
case 400:
error.message = '错误请求'
break;
case 401:
error.message = '未授权,请重新登录'
break;
case 403:
error.message = '拒绝访问'
break;
case 404:
error.message = '请求错误,未找到该资源'
// window.location.href = "/NotFound"
break;
case 405:
error.message = '请求方法未允许'
break;
case 408:
error.message = '请求超时'
break;
case 500:
error.message = '服务器端出错'
break;
case 501:
error.message = '网络未实现'
break;
case 502:
error.message = '网络错误'
break;
case 503:
error.message = '服务不可用'
break;
case 504:
error.message = '网络超时'
break;
case 505:
error.message = 'http版本不支持该请求'
break;
default:
error.message = `连接错误${error.response.status}`
}
} else {
// 超时处理
if (JSON.stringify(error).includes('timeout')) {
error.message = '服务器响应超时,请刷新当前页'
} else {
error.message = '连接服务器失败'
}
} //提示
ElMessage.error(error.message) /***** 处理结束 *****/
return Promise.resolve(error)
}) export default axiosInstance
request-wrapper.ts
api文件夹下 创建 api.ts 接口配置
/*
* @description: 请求接口 配置
* @Author: Jay
* @Date: 2023-04-11 13:24:41
* @LastEditors: Jay
* @LastEditTime: 2023-09-04 13:30:09
*/ //导入 Axios 请求
import request from '@/utils/request'
//其他配置
import operate from '@/utils/operate'; // 官网接口
export const homePost = (data?: any) => {
return request({
url: '/api/index',
method: 'post',
data,
//登录token
isToken: operate.isToken(),
//加载动画是否启动
ifLoading: true,
//是否允许重复请求
repeatRequest: false,
})
} /*
请求配置与使用 * 请求 方式
export const 名字 = (data: any) =>
request.post("接口", data, {
直接为空
注:只能配置 AxiosRequestConfig 里有的参数名 可不用配置
}); *使用 方法
*引入
import {
名字
} from "../api/api"
*生命周期中 请求
名字({请求参数}).then((res) => {
console.log(res)
})
*/
api.ts
开始请求
<script lang="ts" setup>
import { onMounted } from "vue";
import { homePost } from "@/api/api"; //生命周期
onMounted(() => {
homePost().then((res) => {
console.log("第一次", res);
});
homePost().then((res) => {
console.log("第二次", res);
});
});
</script>
请求结果
第一次请求被拦截,只有第二次成功返回
3.3 operate.ts 方法
主要放置一些 全局参数与方法。
在页面中可以通过 import operate from "@/utils/operate"
导入使用,也可以在main.ts中全局配置。
/*
* @description: 全局js
* @Author: Jay
* @Date: 2023-09-04 13:53:47
* @LastEditors: Jay
* @LastEditTime: 2023-09-04 13:55:44
*/ // vuex 数据
import store from '../store/index' //接口地址
const baseUrl = () => {
if (process.env.NODE_ENV == "development") {
//开发环境
return "";
} else {
//正式环境
return "";
}
} //获取用户token
const isToken = () => {
if (store.state.Authorization != '') {
return store.state.Authorization
}
return '';
} /* eslint-disable */ /*
格式化时间 加上时分秒
num: 后台时间格式
type: 'YY-MM-DD' 年月日 ,'HH-MM-SS' 时分秒 ,不传 年月日时分秒
*/
const happenTime = (num: any, type: string) => {
let date = new Date(num * 1000);
//时间戳为10位需*1000,时间戳为13位的话不需乘1000
let y: any = date.getFullYear();
let MM: any = date.getMonth() + 1;
MM = MM < 10 ? ('0' + MM) : MM; //月补0
let d: any = date.getDate();
d = d < 10 ? ('0' + d) : d; //天补0
let h: any = date.getHours();
h = h < 10 ? ('0' + h) : h; //小时补0
let m: any = date.getMinutes();
m = m < 10 ? ('0' + m) : m; //分钟补0
let s: any = date.getSeconds();
s = s < 10 ? ('0' + s) : s; //秒补0
if (type === 'YY-MM-DD') {
//年月日
return y + '-' + MM + '-' + d;
} else if (type === 'HH-MM-SS') {
//时分秒
return h + ':' + m + ':' + s;
} else {
//全部
return y + '-' + MM + '-' + d + ' ' + h + ':' + m + ':' + s;
}
}
/* eslint-enable */ // 页面回到顶部(滚动效果)
/*
使用方法
//监听滚动事件
window.addEventListener("scroll", proxy.$operate.handleScroll, {
once: true,
});
*/
const handleScroll = () => {
let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
console.log(scrollTop, "scrollTop");
if (scrollTop > 0) {
const timeTop = setInterval(() => {
document.documentElement.scrollTop = document.body.scrollTop = scrollTop -= 50; //一次减50往上滑动
if (scrollTop <= 0) {
clearInterval(timeTop);
}
}, 10); //定时调用函数使其更顺滑
}
}; export default {
baseUrl,
isToken,
happenTime,
handleScroll
}
operate.ts
vue3+ts Axios封装—重复请求拦截的更多相关文章
- Vue axios封装 实现请求响应拦截
封装 axios.js import axios from 'axios' import { baseURL } from '@/config' class HttpRequest { constru ...
- axios封装,使用拦截器统一处理接口
1.项目路径下,引入axios.qs依赖 npm install axios npm install qs 2.在项目的src路径下新建一个commJs文件夹,在commJs文件夹里新建aps.js和 ...
- axios取消重复请求与更新token并续订上次请求
一.问题引入 当用户发起一个请求时,判断token是否已过期,若已过期则先调refreshToken接口,拿到新的token后再继续执行之前的请求. 难点:当同时发起多个请求,token 过期会调用多 ...
- AXIOS构建请求处理全局loading状态&&AXIOS避免重复请求loading多次出现
一般情况下,在 vue 中结合 axios 的拦截器控制 loading 展示和关闭,是这样的:在 App.vue 配置一个全局 loading. <div class="app&qu ...
- vue项目实践-添加axios封装api请求
安装 axios npm install axios --save 创建实例 (utils/fetch.js) axios 默认提交格式为:application/json 可使用 qs 模块(需要安 ...
- axios构建请求池处理全局loading状态&&axios避免重复请求
很多时候我们能够看到类似进度条一样的东西在页面顶部进行加载,代表页面是否加载完成,或者其他的loading效果,我们当然不可能通过promise.all来讲所有的请求合并到一起然后进行处理,这个时候我 ...
- axios封装http请求
import axios from 'axios' const HTTP_TIMEOUT = 15000; export function httpPost(url, params = {},head ...
- vue+axois 封装请求+拦截器(请求锁+统一错误)
需求 封装常用请求 拦截器-请求锁 统一处理错误码 一.封装常用的请求 解决痛点:不要每一个模块的api都还要写get,post,patch请求方法.直接将这些常用的方法封装好. 解决方案:写一个类 ...
- vue axios封装以及API统一管理
在vue项目中,每次和后台交互的时候,经常用到的就是axios请求数据,它是基于promise的http库,可运行在浏览器端和node.js中.当项目越来越大的时候,接口的请求也会越来越多,怎么去管理 ...
- vue3.0+vite+ts项目搭建-axios封装(六)
封装方式一 import axios from 'axios' import qs from 'qs' import { Toast } from 'vant' import Lockr from ' ...
随机推荐
- 洛谷 P8742题解
简单版(P2347)传送门 原题传送门 有一道类似的题目(P2347),先扯一扯~ 1.P2347 题目分析 动态规划入门题(01背包可行性问题)~ 我们设 \(dp_j\) 为能否用砝码称出 \(j ...
- v8 setup
记录下笔者本人搭建v8环境的过程 环境:处于一些原因笔者选择在kali2023上搭建v8,kali上可以搭建成功但是调试脚本加载有问题,fuck kali,还是ubuntu好,笔者使用了ubuntu2 ...
- Java(break、continue、label)
1.break break在任何循环语句的主体部分,均可用break控制循环的流程.break用于强行退出循环,不执行循环中剩余的语句.(break语句也在switch语句中使用) 例如:循环输出10 ...
- Win10安装cuda11.0+cudnn8.0(这是配套的)
首先你要知道你的电脑显卡能支持的cuda最大版本: 如下下图所示,支持最大版本为cuda11.0.228版本! 一.下载 Win10 64 位 下载cudnn8.0 链接:https://pan.ba ...
- C++面试八股文:了解位运算吗?
某日二师兄参加XXX科技公司的C++工程师开发岗位第12面: 面试官:了解位运算吗? 二师兄:了解一些.(我很熟悉) 面试官:请列举以下有哪些位运算? 二师兄:按位与(&).按位或(|).按位 ...
- NVIDIA Maxine Video Effects SDK 編程指南 - 实践小记
NVIDIA Maxine Video Effects SDK 編程指南 - 实践小记 本篇博客重点只说Video Effect的部分,此外还有Audio Effect的部分.还有AR部分,不在本篇范 ...
- 尚医通-day12【token续期和就诊人管理】(内附源码)
页面预览 就诊人管理 就诊人列表 添加就诊人 查看就诊人 ![image-20230225060710 管理员系统用户管理 前面我们完成了用户登录.用户认证与就诊人管理,现在我们需要把这些信息在我们的 ...
- 使用 Easysearch 还原 Elasticsearch 快照数据
本文主要验证 Elasticsearch 快照在 Easysearch 中进行数据恢复. 准备测试数据 索引 别名 模版 生命周期策略 创建快照 PUT /_snapshot/my_backup { ...
- 为什么要重写equals要重写hashcode方法
Java 比较(==, equals) 一.= = ==:比较两个对象的引用是否是同一个地址 二.equals object中equals方法调用的就是==,可以在其他类中重写该方法. 三.为什么要重 ...
- EnhancingDecisionTreeswithGeographicInformationSystemsa
目录 引言 在计算机科学领域,地理信息系统和( geographical information systems, GIS)已经成为了一个非常受欢迎的工具.GIS 可以用来处理和存储大量的地理数据,支 ...