Vue3 + Ts + Vite 封装一套企业级axiso全流程

9a69fede8b2044a79dd834e3e48f20b4.png前期回顾f8e3cc1a0f694ac2b665ca2ad14c49d7.png 

从零搭建 Vue3 + VIte + Ts 项目 —— 并集成eslint 、prettier、stylelint、husky、lint-staged、pinia、axios、loding、动态路由…_彩色之外的博客-CSDN博客

   实现功能:

  1. 取消重复请求:完全相同的接口在上一个pending状态时,自动取消下一个请求

  2. 请求失败自动重试: 接口请求后台异常时候, 自动重新发起多次请求, 直到达到所设次数

  3. 请求接口数据缓存: 接口在设定时间内不会向后台获取数据, 而是直接拿本地缓存

  4. 父页面单独取消当前请求、并发取消指定请求

  5. 父页面取消所有请求

  6. 更多功能根据你的需求自定制

目录

  🌍 第一 配置 vite.config.ts 基地址: 

  🤖 第二 配置环境变量:

 🛹 第三  配置ts类型

 🪂 第四 封装本地存储

 🎋 第五 封装axios: 

 👀 第六 页面使用:


🌍 第一 配置 vite.config.ts 基地址: 

  🤖 第二 配置环境变量:

🛹 第三  配置ts类型

src/type/axiso.d.ts

/* eslint-disable */
import * as axios from 'axios';// 扩展 axios 数据返回类型,可自行扩展
declare module 'axios' {export interface AxiosResponse<T = any> {code: number;data: T;message: string;type?: string;/* [key: string]: T; 这段代码是定义了一个索引签名,它表示可以使用任意字符串作为key,并且对应的值的类型是T。索引签名允许在对象中使用动态的属性,也就是说,在定义AxiosResponse接口时,除了预定义的code、data、message属性,还可以添加其他任意属性,且属性的值的类型是T。*/[key: string]: T;}export interface AxiosRequestConfig<T = any> {retry?: number;retryDelay?: number;cache?: boolean;cacheTimestamp?: number;[key: string]: T;}export interface AxiosError<T = any> {config: AxiosRequestConfig<T>;code?: string;request?: any;response?: AxiosResponse<T>;isAxiosError: boolean;retry?: number;retryDelay?: number;retryCount: number;cache?: boolean;cacheTimestamp?: number;[key: string]: T;}export interface CancelTokenSource<T = any> {token: CancelToken;cancel: Canceler;isFinished?: boolean;[key: string]: T;}
}

 🪂 第四 封装本地存储

/*** window.localStorage 浏览器永久缓存* @method set 设置永久缓存* @method get 获取永久缓存* @method remove 移除永久缓存* @method clear 移除全部永久缓存*/
export const Local = {// 设置永久缓存set(key: string, val: any) {window.localStorage.setItem(key, JSON.stringify(val));},// 获取永久缓存get(key: string) {let json = <string>window.localStorage.getItem(key);// !null为trueif (!json) return null;return JSON.parse(json);},// 移除永久缓存remove(key: string) {window.localStorage.removeItem(key);},// 移除全部永久缓存clear() {window.localStorage.clear();},
};/*** window.sessionStorage 浏览器临时缓存* @method set 设置临时缓存* @method get 获取临时缓存* @method remove 移除临时缓存* @method clear 移除全部临时缓存*/
export const Session = {// 设置临时缓存set(key: string, val: any) {window.sessionStorage.setItem(key, JSON.stringify(val));},// 获取临时缓存get(key: string) {let json = <string>window.sessionStorage.getItem(key);if (!json) return null;return JSON.parse(json);},// 移除临时缓存remove(key: string) {window.sessionStorage.removeItem(key);},// 移除全部临时缓存clear() {window.sessionStorage.clear();},
};

  🎋 第五 封装axios: 

新建 \src\api 文件夹,里面有三个ts文件,request.ts 封装axios统一请求,requestMethod.ts   封装的是请求方法,api.ts 封装的是api接口,方便统一管理不至于api接口分散项目各处造成不易维护。

src\api\request.ts

import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, CancelTokenSource } from 'axios';
/* 1. 取消重复请求:完全相同的接口在上一个pending状态时,自动取消下一个请求 2. 请求失败自动重试: 接口请求后台异常时候, 自动重新发起多次请求, 直到达到所设次数 3. 请求接口数据缓存: 接口在设定时间内不会向后台获取数据, 而是直接拿本地缓存4. 父页面单独取消当前请求5. 父页面取消所有请求
*/// 导入 element-plus 中的消息和弹框组件
import { ElMessage, ElMessageBox } from 'element-plus';
// 导入 storage.ts 中的 Session 对象
import { Session } from '/@/utils/storage';
// 导入 main.ts 中的 app 对象 用于Loading组件的显示和隐藏
import app from '/@/main';// 创建 Axios 实例
const service: AxiosInstance = axios.create({baseURL: import.meta.env.VITE_API_URL, // 设置基础 URLtimeout: 5000, // 设置超时时间headers: { 'Content-Type': 'application/json' }, // 设置请求头
});// handlerRequest Start --------------------------------------------------------------------------// cacheTimestamp用于判断是否存在相同缓存的请求
let cacheTimestampFlag = 0;
// requestKey用于缓存接口函数 判断是否存在相同的请求
let requestKey = '';
// 创建一个存储请求的Map对象
const pendingRequests: Map<string, CancelTokenSource> = new Map();
// 取消重复请求的方法
const cancelDuplicateRequest = (config: AxiosRequestConfig): void => {// 生成请求的唯一标识requestKey = `${config.method}-${config.url}`;// 如果已经存在该请求,则取消该请求if (pendingRequests.has(requestKey)) {const cancelToken = pendingRequests.get(requestKey);cancelToken?.cancel('进行中的重复请求被拦截,请您等待当前请求完成后再发起请求');}// 生成一个取消请求的标识const cancelToken = axios.CancelToken.source();// 将该请求保存到 pendingRequests 中pendingRequests.set(requestKey, cancelToken);// 设置取消请求的标识config.cancelToken = cancelToken.token;// 设置缓存时间if (config.cacheTimestamp) {cacheTimestampFlag = eval(`1000 * 60 * ${config.cacheTimestamp}`);}// 如果本地有缓存数据,直接返回缓存数据,不经过请求拦截if (config.cache) requestIsCache();
};
// 缓存接口函数 - 注意发起请求判断是否存在相同url
async function requestIsCache(): Promise<any> {// 获取本地存储的所有键const keys = Object.keys(sessionStorage);if (requestKey) {// 停留时间 > 缓存时间阈值const isCache = Date.now() - Session.get(requestKey)?.cacheTimestamp > cacheTimestampFlag;// console.log('是否有key', keys.includes(requestKey));// console.log('停留时间', Date.now() - Session.get(requestKey)?.cacheTimestamp);// console.log('判断阈值', cacheTimestampFlag);// 如果包含 urlKey 并且 缓存未过期if (keys.includes(requestKey) && !isCache) {// 直接返回本地缓存数据const cacheData = Session.get(requestKey);return Promise.resolve(cacheData);} else {// 清除缓存Session.remove(requestKey);// 不存在缓存 或 缓存已过期return Promise.reject('不存在缓存 或 缓存已过期');}}
}// 封装提示函数 (token过期、重复登录)
const tipError = (value: string, title: string) => {ElMessageBox.alert(value, title, {confirmButtonText: title,type: 'warning',}).then(() => {Session.clear(); // 清除临时缓存// 清除cookiedocument.cookie.split(';').forEach(function (c) {document.cookie = c.replace(/^ +/, '').replace(/=.*/, '=;expires=' + new Date().toUTCString() + ';path=/');});window.location.reload(); // 刷新页面});
};// 请求失败自动重试的方法
const retryFailedRequest = async (error: AxiosError): Promise<any> => {const config = error;// 如果没有设置重试次数 或 已经达到最大重试次数,则直接返回错误if (!config || !config.retry || config.retryCount >= config.retry) {return Promise.reject(config);}// 设置重试次数关闭阈值config.retryCount = config.retryCount || 0;// 重试次数自增config.retryCount += 1;// 设置重试延时const delay = config.retryDelay || 1000;// 延时处理await new Promise<void>((resolve) => {setTimeout(() => resolve(), delay);});// console.log('🤺🤺  🚀 ==>:', service(config));return service(config);
};
// handlerRequest End --------------------------------------------------------------------------// Axios 的请求拦截器期望返回一个配置对象,而不是响应对象。如果你试图返回一个响应对象,Axios 将会抛出一个错误。
service.interceptors.request.use(async (config: AxiosRequestConfig) => {// 在发送请求之前做些什么?const token = Session.get('token');if (token) config.headers!['token'] = token; // 在请求头中添加 token// 取消重复请求cancelDuplicateRequest(config);app.config.globalProperties.$smallLoading.showLoading();return config;},async (error) => {// 对请求错误做些什么?app.config.globalProperties.$smallLoading.hideLoading();// 请求失败重试await retryFailedRequest(error);return Promise.reject(error);}
);// 响应拦截器
service.interceptors.response.use((response) => {// 对响应数据做点什么? 这里只返回成功响应数据!const { config, data } = response;// 给 pendingRequests 标记一个isFinished为true 请求完成的标识const responseKey = `${config.method}-${config.url}`;const request = pendingRequests.get(responseKey);if (request && request.token) {pendingRequests.set(responseKey, { ...request, isFinished: true });}const cacheKey = `${config.method}-${config.url}`;app.config.globalProperties.$smallLoading.hideLoading();// 判断是否有缓存if (config.cache) {const cachedResponse = Session.get(cacheKey);if (cachedResponse) {return cachedResponse;} else {// 接口有 cache 参数,且缓存不存在,则缓存接口数据,并插入当前时间戳data.cacheTimestamp = new Date().getTime();Session.set(cacheKey, data);return data;}} else {return data;}},(error) => {// 对响应错误数据做点什么?这里只显示错误消息!app.config.globalProperties.$smallLoading.hideLoading();/* axios.isCancel(error) 是 Axios 库中的一个方法,用于判断一个错误对象是否是由于请求取消导致的。当使用 axios.CancelToken 取消请求时,会抛出一个带有一个 message 属性的错误对象。axios.isCancel(error) 的作用就是判断这个错误对象的类型,如果是由请求取消导致的错误,则返回 true,否则返回 false。console.log('打印cancelToken.cancel('xxx')传入来的值', error.message);*/if (axios.isCancel(error)) {// 只提示请求取消有主动填写的消息 如:cancelToken.cancel('xxx')if (error.message !== 'canceled') ElMessage.error(requestKey + ' 🤖 ' + error.message);} else {// 响应失败重试retryFailedRequest(error);// 不是由请求取消导致的错误let errorMessage; // 错误提示变量let statusData = error.response?.data; // 错误data数据const describeForNameMap = [[() => error.message.indexOf('timeout') !== -1,() => (errorMessage = '网络超时 🤖'),],[() => error.message === 'Network Error', () => (errorMessage = '网络连接错误 🤪')],[() => statusData?.code === 100010021,() => {// 100010021 重复登录errorMessage = statusData.message;tipError(errorMessage, '重复登录');},],[() => statusData?.code === 100010011,() => {// 100010011 token过期errorMessage = statusData.message;tipError(errorMessage, '登录过期');},],// 否则 显示错误消息,这里要根据后台返回的数据结构来定[() => statusData, () => (errorMessage = statusData.message)],];// 获取符合条件的子数组const getDescribe = describeForNameMap.find((item) => item[0]());// 执行子数组中的函数getDescribe && getDescribe[1]();ElMessage.error(errorMessage); // 显示错误消息}}
);
// 取消全部请求的方法
export const cancelAllRequest = (): void => {// 创建一个标记 是否取消成功,初始值为falselet hasCancelled = false;// 遍历所有待处理的请求pendingRequests.forEach((value) => {// 如果请求还没有完成if (!value.isFinished) {// 取消请求value.cancel();// 将标记设为truehasCancelled = true;}});// 清空待处理请求的集合pendingRequests.clear();// 至少取消了一个请求,显示提示,防止都是成功请求点击取消按钮时也提示if (hasCancelled) {ElMessage.success('成功取消全部请求');}
};// 取消当前请求的方法
export const cancelCurrentRequest = (payloadCurrentKey: string = requestKey): void => {// 遍历所有待处理的请求pendingRequests.forEach((value, key) => {if (key === payloadCurrentKey) {value.cancel();pendingRequests.delete(key);ElMessage.success('成功取消当前请求');}});
};// 导出 service将其命名为axios , requestIsCache 用于判断是否有缓存
export { service as axios, requestIsCache };

src\api\requestMethod.ts

import { axios } from './request';
// post使用data接受参数,get使用params接受参数
// 如果是post请求,但是参数是在url上的,那么就要使用params接受参数,否则使用data接受参数
// put 也相当与post请求,如果报参数错误,就是接受参数的请求体错了post/put用data,get请求用params
type method = 'GET' | 'POST' | 'PUT' | 'DELETE';
// 规定缓存时间戳的类型只能 1 - 5 分钟
type cacheTimestamp = 1 | 2 | 3 | 4 | 5;/*** @name request 配置* @param { string } - method 请求方法* @param { string } - url   请求地址* @param { object } - data/params 请求参数* @param { number } - retry  重试次数* @param { number } - retryDelay 重试延迟* @param { boolean } -  cache 是否缓存* @param { number }  - cacheTimestamp 缓存过期 1-5分钟* @createDate 2023/08/09 13:12:08* @lastFixDate 2023/08/09 13:12:08*/
interface requestConfig {method: method;url: string;data?: object;params?: object;retry?: number;retryDelay?: number;cache?: boolean;cacheTimestamp?: cacheTimestamp;
}function request({method = 'GET',url,data = {},params = {},retry,retryDelay,cache,cacheTimestamp = 1,
}: requestConfig) {return axios({method,url,data,params,retry,retryDelay,cache,cacheTimestamp,});
}export default request;

src\api/auth-manage/menu.ts

// 导入axios中的AxiosResponse的泛型接口
import { AxiosResponse } from 'axios';
// 导入封装的axios请求方法
import request from '/@/utils/requestMethod';// 获取菜单树接口: 不包含菜单中的按钮
export const getMenuTree = (): Promise<AxiosResponse<any>> =>request({method: 'POST',url: '/menu/queryMenuTree',cache: true,cacheTimestamp: 1,});

👀 第六 页面使用:

<script setup lang="ts" name="importGeneration">
import { getMenuTree } from '/@/api/auth-manage/menu';
import { getUserInfo } from '/@/api/auth-manage/user';
import { ElMessage } from 'element-plus';/* requestIsCache - 判断请求是否开启了缓存cancelAllRequest - 取消所有请求cancelCurrentRequest - 取消当前请求
*/
import { requestIsCache, cancelAllRequest, cancelCurrentRequest } from '/@/utils/request';// 封装请求错误提示 http状态是200 但是code不是200 返回数据是错误的需要处理
function tipError<T extends { code: number; message: string }>(res: T) {if (res.code !== 200) {ElMessage.error(res.message);return;}
}
// 发起 getMenuTree
const handleClick = async () => {// 缓存函数,如果在接口开启了cache: true,需要在请求前调用此函数await requestIsCache().then((res) => {if (!res) return;tipError(res);ElMessage.success('本地数据请求成功');}).catch(() => {getMenuTree().then((res) => {if (!res) return;tipError(res);ElMessage.success(res.message);});});
};// 取消 getMenuTree
const handleCance = () => {// 在适当的时机调用取消请求(例如点击取消按钮),不传参数默认取消最后一条请求cancelCurrentRequest('post-/menu/queryMenuTree');
};// 取消 getUserInf
const handleCance1 = () => {cancelCurrentRequest('get-/user/info');
};// 发起 getUserInf
const handleClick1 = async () => {await getUserInfo().then((res) => {if (!res) return;tipError(res);ElMessage.success(res.message);});
};// 取消所有请求
function handleCancelAll() {cancelAllRequest();
}
</script><template><div><!-- 发起 --><el-button type="primary" @click="handleClick">发起 getMenuTree axios</el-button><!-- 取消 --><el-button type="danger" @click="handleCance">取消 getMenuTree</el-button><!-- 发起 --><el-button type="primary" @click="handleClick1">发起 getUserInf axios</el-button><!-- 取消 --><el-button type="danger" @click="handleCance1">取消 getUserInf</el-button><el-button type="danger" @click="handleCancelAll">取消所有请求</el-button></div>
</template>

全文结束,所有代码都在文中,最上面的链接中也有原项目

7730e2bd39d64179909767e1967da702.jpeg

 _______________________________  期待再见  _______________________________ 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/30840.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

企业服务器被devos勒索病毒攻击后怎么处理,devos勒索病毒如何攻击的

众所周知&#xff0c;科学技术是第一生产力&#xff0c;科学技术的发展给企业与人们的生活带来了极大变化&#xff0c;但随之而来的网络安全威胁也不断增加。最近&#xff0c;我们收到很多企业的求助&#xff0c;企业的计算机服务器遭到了devos勒索病毒的攻击&#xff0c;导致企…

oracle积累增量和差异增量

积累增量和差异增量&#xff1a; 对于 RMAN 来说&#xff0c;积累增量备份和差异增量备份都是增量备份的一种形式&#xff0c;它们之间的区别在于备份的范围和备份集的方式。 积累增量备份&#xff1a;在进行积累增量备份时&#xff0c;RMAN 会备份自最后一次完全备份或增量备…

【Mybatis】调试查看执行的 SQL 语句

1. 问题场景&#xff1a; 记录日常开发过程中 Mybatis 调试 SQL 语句&#xff0c;想要查看Mybatis 中执行的 SQL语句&#xff0c;导致定位问题困难 2. 解决方式 双击shift找到mybatis源码中的 MappedStatement的getBoundSql()方法 public BoundSql getBoundSql(Object para…

selenium爬虫,配置谷歌浏览器的driver

用selenium爬虫时&#xff0c;明明已经安装了selenium模块&#xff0c;程序却运行不了。在使用selenium之前必须先配置浏览器对应版本的webdriver 本文主要涉及驱动有问题driver 网上有很多手动的方法&#xff08;查看谷歌浏览的版本然后在其他博主分享的webdriver中下载与自己…

Mybatis Plus条件构造器LambdaQueryWrapper

官网地址 Mybatis Plus条件构造器LambdaQueryWrapper 目前数据库数据情况&#xff0c;User表 iduser_namebirthdaysexaddress1张12023-08-10男123163.com2李12023-08-10女222163.com3张22023-08-10女999163.com4张32023-08-10男9994qq.com ## 简单介绍 如何使用各种场景 方法…

论文笔记:SUPERVISED CONTRASTIVE REGRESSION

2022arxiv的论文&#xff0c;没有中&#xff0c;但一作是P大图班本MIT博&#xff0c;可信度应该还是可以的 0 摘要 深度回归模型通常以端到端的方式进行学习&#xff0c;不明确尝试学习具有回归意识的表示。 它们的表示往往是分散的&#xff0c;未能捕捉回归任务的连续性质。…

gateway做token校验

本文使用springcloud的gateway做token校验 登录的本质&#xff1a;拿用户名和密码 换 token。 token会返回给浏览器&#xff08;存储&#xff09;&#xff0c;当访问的时候&#xff0c;携带token 发起请求。 token校验图 引入redis依赖 <dependency><groupId>or…

2000-2022年全国地级市乡村振兴测算数据(30个指标)

1、时间&#xff1a;2000-2022年 2、来源&#xff1a;城市统计NJ、各地区统计NJ 3、范围&#xff1a;地级市 4、指标&#xff1a;乡村振兴指数、人均农业机械总动力&#xff08;千瓦&#xff09;、粮食综合生产能力&#xff08;万吨&#xff09;、农业劳动生产率&#xff08…

Vue2:路由

Vue2&#xff1a;路由 Date: May 28, 2023 Sum: vue-router基本使用、高级用法 单页面应用程序 概念&#xff1a;SPA【Single Page Application】是指所有的功能都在一个html页面上实现 案例&#xff1a; 单页应用网站&#xff1a; 网易云音乐 https://music.163.com/ 多页…

Idea使用Docker插件实现maven打包自动构建镜像

Docker 开启TCP 服务 vi /lib/systemd/system/docker.service改写以下内容 ExecStart/usr/bin/dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock重启服务 #重新加载配置文件 systemctl daemon-reload #重启服务 systemctl restart docker.service此时docker已…

大数据课程I1——Kafka的概述

文章作者邮箱&#xff1a;yugongshiyesina.cn 地址&#xff1a;广东惠州 ▲ 本章节目的 ⚪ 了解Kafka的概念&#xff1b; ⚪ 掌握Kafka的配置与启动&#xff1b; 一、简介 1. 基本概念 Apache kafka 是一个分布式数据流平台。可以从如下几个层面来理解&#x…

elasticsearch-head可视化安装

一、前言 elasticsearch-head 是用于监控 Elasticsearch 状态的客户端插件&#xff0c;包括数据可视化、执行增删改查操作等。 elasticsearch是通过API方式进行管理的&#xff0c;因此也可以使用postman等工具操作elasticsearch。 二、安装 lasticsearch-head插件是使用Jav…

C++核心编程——函数高级、类和对象

3 函数提高 3.1 函数默认参数 在C中&#xff0c;函数的形参列表中的形参是可以有默认值的。 语法&#xff1a;返回值类型 函数名 &#xff08;参数默认值&#xff09;{} 注意事项&#xff1a; 1、如果函数的参数列表中某个参数已经有了默认参数&#xff0c;那么从这个参…

cve-2021-2394 weblogic反序列化漏洞分析

前几天weblogic 7月例行更新中&#xff0c;修复了一个Rce漏洞。该漏洞性质属于绕过之前的反序列化漏洞补丁。要了解这个漏洞的原因&#xff0c;我们首先要学习其他几个漏洞的原理。 一 weblogic 反序列化绕过指南 本章节只是大概讲解一下如何绕过weblogic反序列化漏洞的补丁。…

解决selenium的“can‘t access dead object”错误

目录 问题描述 原因 解决方法 示例代码 资料获取方法 问题描述 在python执行过程中&#xff0c;提示selenium.common.exceptions.WebDriverException: Message: TypeError: cant access dead object 原因 原因是代码中用到了frame,获取元素前需要切换到frame才能定位到…

苍穹外卖day11笔记

今日首先介绍前端技术Apache ECharts&#xff0c;说明后端需要准备的数据&#xff0c;然后讲解具体统计功能的实现&#xff0c;包括营业额统计、用户统计、订单统计、销量排名。 一、ECharts 是什么 ECharts是一款基于 Javascript 的数据可视化图表库。我们用它来展示图表数…

一次面试下来Android Framework 层的源码就问了4轮

说起字节跳动的这次面试经历&#xff0c;真的是现在都让我感觉背脊发凉&#xff0c;简直被面试官折磨的太难受了。虽然已经工作了七年&#xff0c;但是也只是纯粹的在写业务&#xff0c;对底层并没有一个很深的认识&#xff0c;这次面试经历直接的让我感受到我和那些一线大厂开…

Client not connected, current status:STARTING

今天项目集成Seata时遇到一个奇怪的异常&#xff0c;在此记录一下。 Linux环境安装Seata&#xff0c;使用Nacos作为配置中心、注册中心&#xff1b; Linux已开放端口&#xff1a;8848、7091、8091 在我Windows环境下可以看到Nacos运行正常&#xff0c;Seata运行也正常&#…

【腾讯云 Cloud Studio 实战训练营】用于编写、运行和调试代码的云 IDE泰裤辣

文章目录 一、引言✉️二、什么是腾讯云 Cloud Studio&#x1f50d;三、Cloud Studio优点和功能&#x1f308;四、Cloud Studio初体验&#xff08;注册篇&#xff09;&#x1f386;五、Cloud Studio实战演练&#xff08;实战篇&#xff09;&#x1f52c;1. 初始化工作空间2. 安…

学习笔记-JAVAJVM-JVM的基本结构及概念

申明&#xff1a;文章内容是本人学习极客时间课程所写&#xff0c;文字和图片基本来源于课程资料&#xff0c;在某些地方会插入一点自己的理解&#xff0c;未用于商业用途&#xff0c;侵删。 原资料地址&#xff1a;课程资料 什么是JVM 原文连接&#xff1a; 原文连接 JVM是J…