经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » iOS » 查看文章
vue3+ts Axios封装—重复请求拦截
来源:cnblogs  作者:虚乄  时间:2023/9/6 10:57:10  对本文有异议

创建好vue3项目

1.安装Axios与Element Plus

Axios安装

  1. npm install axios

Element Plus 安装

官网入口:https://element-plus.gitee.io/zh-CN/

  1. 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 增加自定义参数

  1. declare module 'axios' {
  2. //请求自定义参数
  3. interface AxiosRequestConfig {
  4. // 是否显示加载框
  5. ifLoading?: boolean
  6. // 是否允许重复请求
  7. repeatRequest?: boolean
  8. // 登录 token
  9. isToken?: any;
  10. }
  11. }

3.1重复请求判断

通过配置repeatRequest是否允许重复请求,来开启判断。主要在api.ts中配置。

每一次请求创建一个key,判断是否存在,如存在执行.abort()取消当前请求,

不存在pendingMap中新增一个key。

通过AbortController来进行手动取消。

主要代码

  1. //格式化请求链接
  2. function getRequestKey(config: AxiosRequestConfig) {
  3. const { url, method, data, params } = config,
  4. //字符串化参数
  5. dataStr = JSON.stringify(data) || '',
  6. paramsStr = JSON.stringify(params) || '',
  7. //记得这里一定要处理 每次请求都掉会变化的参数(比如每个请求都携带了时间戳),否则二个请求的key不一样
  8. key = [method, url, dataStr, paramsStr].join("&");
  9. return key;
  10. }
  11.  
  12. //创建存储 key 的 集合
  13. const pendingMap = new Map()
  14.  
  15. //是否重复请求key
  16. function setPendingMap(config: AxiosRequestConfig) {
  17. //手动取消
  18. const controller = new AbortController()
  19. config.signal = controller.signal
  20. const key = getRequestKey(config)
  21. //判断是否存在key 存在取消请求 不存在添加
  22. if (pendingMap.has(key)) {
  23. // abort取消请求
  24. pendingMap.get(key).abort()
  25. //删除key
  26. pendingMap.delete(key)
  27. } else {
  28. pendingMap.set(key, controller)
  29. }
  30. }

在接口消息提示时,通过 axios.isCancel(error) 过滤掉已取消的请求,

  1. //拦截掉重复请求的错误,中断promise执行
  2. if (axios.isCancel(error)) return []

3.2 axios完整代码

api文件夹下 创建 request-wrapper.ts Axios封装
  1. /*
  2. * @description: 请求封装
  3. * @Author: Jay
  4. * @Date: 2023-04-11 13:24:41
  5. * @LastEditors: Jay
  6. * @LastEditTime: 2023-09-04 15:04:28
  7. */
  8.  
  9. // 导入axios
  10. import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";
  11. // 使用element-ui ElMessage做消息提醒 ElLoading加载
  12. import { ElMessage, ElLoading } from "element-plus";
  13. //请求头
  14. import operate from "@/utils/operate"
  15.  
  16. //加载配置
  17. let loadingInstance: { close: () => void };
  18. let requestNum = 0;
  19. //加载动画
  20. const addLoading = () => {
  21. // 防止重复弹出
  22. requestNum++;
  23. if (requestNum === 1) {
  24. loadingInstance = ElLoading.service({ fullscreen: true });
  25. }
  26. }
  27. // 关闭 加载动画
  28. const cancelLoading = () => {
  29. requestNum--;
  30. if (requestNum === 0) loadingInstance?.close();
  31. }
  32. //格式化请求链接
  33. function getRequestKey(config: AxiosRequestConfig) {
  34. const { url, method, data, params } = config,
  35. //字符串化参数
  36. dataStr = JSON.stringify(data) || '',
  37. paramsStr = JSON.stringify(params) || '',
  38. //记得这里一定要处理 每次请求都掉会变化的参数(比如每个请求都携带了时间戳),否则二个请求的key不一样
  39. key = [method, url, dataStr, paramsStr].join("&");
  40. return key;
  41. }
  42. //创建存储 key 的 集合
  43. const pendingMap = new Map()
  44. //是否重复请求key
  45. function setPendingMap(config: AxiosRequestConfig) {
  46. // 手动取消请求
  47. const controller = new AbortController()
  48. config.signal = controller.signal
  49. const key = getRequestKey(config)
  50. //判断是否存在key 存在取消请求 不存在添加
  51. if (pendingMap.has(key)) {
  52. // abort取消请求
  53. pendingMap.get(key).abort()
  54. //删除key
  55. pendingMap.delete(key)
  56. } else {
  57. pendingMap.set(key, controller)
  58. }
  59. }
  60. //增加新的请求参数类型
  61. declare module 'axios' {
  62. //请求自定义参数
  63. interface AxiosRequestConfig {
  64. // 是否显示加载框
  65. ifLoading?: boolean
  66. // 是否允许重复请求
  67. repeatRequest?: boolean
  68. // 登录 token
  69. isToken?: any;
  70. }
  71. // 解決 类型“AxiosResponse<any, any>”上不存在属性“code”
  72. // interface AxiosResponse<T = any> {
  73. // // 请求 data 里的一级参数
  74. // code: number;
  75. // time: string;
  76. // msg: string;
  77. // data: T;
  78. // }
  79. }
  80. //创建axios的一个实例
  81. const axiosInstance: AxiosInstance = axios.create({
  82. //接口统一域名
  83. baseURL: operate.baseUrl(),
  84. //设置超时
  85. timeout: 1000 * 30,
  86. //跨域携带cookie
  87. withCredentials: true,
  88. })
  89. // 添加请求拦截器
  90. axiosInstance.interceptors.request.use(
  91. (config) => {
  92. //加载动画
  93. if (config?.ifLoading) addLoading();
  94. //是否判断重复请求
  95. if (!config.repeatRequest) {
  96. setPendingMap(config)
  97. }
  98. //判断是否有token 根据自己的需求判断
  99. const token = config.isToken
  100. // console.log("判断是否有token", token)
  101. if (token != undefined) {
  102. //如果要求携带在参数中
  103. // config.params = Object.assign({}, config.params, operate.uploadParameters())
  104. // 如果要求携带在请求头中
  105. // config.headers = Object.assign({}, config.headers, operate.uploadParameters())
  106. }
  107. return config
  108. },
  109. (error: AxiosError) => {
  110. return Promise.reject(error)
  111. }
  112. )
  113. // 添加响应拦截器
  114. axiosInstance.interceptors.response.use((response: AxiosResponse) => {
  115. const config = response.config
  116. // 关闭加载 动画
  117. if (config?.ifLoading) cancelLoading();
  118. //是否登录过期
  119. if (response.data.code == 400 || response.data.code == 401) {
  120. ElMessage.error("登录过期,请重新登录")
  121. // //清除登录缓存
  122. // store.commit("LOGOUT")
  123. // //返回首页
  124. // setTimeout(() => {
  125. // router.push("/");
  126. // }, 500);
  127. return
  128. }
  129. // 返回参数
  130. return response.data
  131. })
  132. // 错误处理
  133. axiosInstance.interceptors.response.use(undefined, (error) => {
  134. const config = error.config
  135. // 关闭加载 动画
  136. if (config?.ifLoading) cancelLoading();
  137. //拦截掉重复请求的错误,中断promise执行
  138. if (axios.isCancel(error)) return []
  139. /***** 接收到异常响应的处理开始 *****/
  140. if (error && error.response) {
  141. // 1.公共错误处理
  142. // 2.根据响应码具体处理
  143. switch (error.response.status) {
  144. case 400:
  145. error.message = '错误请求'
  146. break;
  147. case 401:
  148. error.message = '未授权,请重新登录'
  149. break;
  150. case 403:
  151. error.message = '拒绝访问'
  152. break;
  153. case 404:
  154. error.message = '请求错误,未找到该资源'
  155. // window.location.href = "/NotFound"
  156. break;
  157. case 405:
  158. error.message = '请求方法未允许'
  159. break;
  160. case 408:
  161. error.message = '请求超时'
  162. break;
  163. case 500:
  164. error.message = '服务器端出错'
  165. break;
  166. case 501:
  167. error.message = '网络未实现'
  168. break;
  169. case 502:
  170. error.message = '网络错误'
  171. break;
  172. case 503:
  173. error.message = '服务不可用'
  174. break;
  175. case 504:
  176. error.message = '网络超时'
  177. break;
  178. case 505:
  179. error.message = 'http版本不支持该请求'
  180. break;
  181. default:
  182. error.message = `连接错误${error.response.status}`
  183. }
  184. } else {
  185. // 超时处理
  186. if (JSON.stringify(error).includes('timeout')) {
  187. error.message = '服务器响应超时,请刷新当前页'
  188. } else {
  189. error.message = '连接服务器失败'
  190. }
  191. }
  192. //提示
  193. ElMessage.error(error.message)
  194. /***** 处理结束 *****/
  195. return Promise.resolve(error)
  196. })
  197. export default axiosInstance
request-wrapper.ts
api文件夹下 创建 api.ts 接口配置
  1. /*
  2. * @description: 请求接口 配置
  3. * @Author: Jay
  4. * @Date: 2023-04-11 13:24:41
  5. * @LastEditors: Jay
  6. * @LastEditTime: 2023-09-04 13:30:09
  7. */
  8.  
  9. //导入 Axios 请求
  10. import request from '@/utils/request'
  11. //其他配置
  12. import operate from '@/utils/operate';
  13. // 官网接口
  14. export const homePost = (data?: any) => {
  15. return request({
  16. url: '/api/index',
  17. method: 'post',
  18. data,
  19. //登录token
  20. isToken: operate.isToken(),
  21. //加载动画是否启动
  22. ifLoading: true,
  23. //是否允许重复请求
  24. repeatRequest: false,
  25. })
  26. }
  27. /*
  28. 请求配置与使用
  29. * 请求 方式
  30. export const 名字 = (data: any) =>
  31. request.post("接口", data, {
  32. 直接为空
  33. 注:只能配置 AxiosRequestConfig 里有的参数名 可不用配置
  34. });
  35. *使用 方法
  36. *引入
  37. import {
  38. 名字
  39. } from "../api/api"
  40. *生命周期中 请求
  41. 名字({请求参数}).then((res) => {
  42. console.log(res)
  43. })
  44. */
api.ts
开始请求
  1. <script lang="ts" setup>
  2. import { onMounted } from "vue";
  3. import { homePost } from "@/api/api";
  4.  
  5. //生命周期
  6. onMounted(() => {
  7. homePost().then((res) => {
  8. console.log("第一次", res);
  9. });
  10. homePost().then((res) => {
  11. console.log("第二次", res);
  12. });
  13. });
  14. </script>

请求结果

第一次请求被拦截,只有第二次成功返回

3.3 operate.ts 方法

主要放置一些 全局参数与方法。

在页面中可以通过 import operate from "@/utils/operate" 导入使用,也可以在main.ts中全局配置。

  1. /*
  2. * @description: 全局js
  3. * @Author: Jay
  4. * @Date: 2023-09-04 13:53:47
  5. * @LastEditors: Jay
  6. * @LastEditTime: 2023-09-04 13:55:44
  7. */
  8.  
  9. // vuex 数据
  10. import store from '../store/index'
  11.  
  12. //接口地址
  13. const baseUrl = () => {
  14. if (process.env.NODE_ENV == "development") {
  15. //开发环境
  16. return "";
  17. } else {
  18. //正式环境
  19. return "";
  20. }
  21. }
  22. //获取用户token
  23. const isToken = () => {
  24. if (store.state.Authorization != '') {
  25. return store.state.Authorization
  26. }
  27. return '';
  28. }
  29. //上传请求头 登录验证
  30. const uploadParameters = () => {
  31. return { 'token': isToken() }
  32. // return { 'Authori-zation': 'Bearer ' + isToken() }
  33. }
  34. /* eslint-disable */
  35.  
  36. /*
  37. 格式化时间 加上时分秒
  38. num: 后台时间格式
  39. type: 'YY-MM-DD' 年月日 ,'HH-MM-SS' 时分秒 ,不传 年月日时分秒
  40. */
  41. const happenTime = (num: any, type: string) => {
  42. let date = new Date(num * 1000);
  43. //时间戳为10位需*1000,时间戳为13位的话不需乘1000
  44. let y: any = date.getFullYear();
  45. let MM: any = date.getMonth() + 1;
  46. MM = MM < 10 ? ('0' + MM) : MM; //月补0
  47. let d: any = date.getDate();
  48. d = d < 10 ? ('0' + d) : d; //天补0
  49. let h: any = date.getHours();
  50. h = h < 10 ? ('0' + h) : h; //小时补0
  51. let m: any = date.getMinutes();
  52. m = m < 10 ? ('0' + m) : m; //分钟补0
  53. let s: any = date.getSeconds();
  54. s = s < 10 ? ('0' + s) : s; //秒补0
  55. if (type === 'YY-MM-DD') {
  56. //年月日
  57. return y + '-' + MM + '-' + d;
  58. } else if (type === 'HH-MM-SS') {
  59. //时分秒
  60. return h + ':' + m + ':' + s;
  61. } else {
  62. //全部
  63. return y + '-' + MM + '-' + d + ' ' + h + ':' + m + ':' + s;
  64. }
  65. }
  66. /* eslint-enable */
  67.  
  68.  
  69. // 页面回到顶部(滚动效果)
  70. /*
  71. 使用方法
  72. //监听滚动事件
  73. window.addEventListener("scroll", proxy.$operate.handleScroll, {
  74. once: true,
  75. });
  76. */
  77. const handleScroll = () => {
  78. let scrollTop = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
  79. console.log(scrollTop, "scrollTop");
  80. if (scrollTop > 0) {
  81. const timeTop = setInterval(() => {
  82. document.documentElement.scrollTop = document.body.scrollTop = scrollTop -= 50; //一次减50往上滑动
  83. if (scrollTop <= 0) {
  84. clearInterval(timeTop);
  85. }
  86. }, 10); //定时调用函数使其更顺滑
  87. }
  88. };
  89. export default {
  90. baseUrl,
  91. isToken,
  92. uploadParameters,
  93. happenTime,
  94. handleScroll
  95. }
operate.ts

 

 

 

原文链接:https://www.cnblogs.com/lovejielive/p/17676856.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号