【场景方案】如何去设计并二次封装一个好用的axios,给你提供一个好的参考(1.0版)

news/2024/5/15 15:35:09/文章来源:https://blog.csdn.net/pagnzong/article/details/130778933

文章目录

  • 前言
  • 文件结构建议
    • 封装的文件结构
    • 接口管理文件
  • 二次封装axios的初始配置
    • init
    • utils
    • webConfig
  • 再封一层
  • 环境配置
  • 使用

前言

以下演示基于vue3与element-plus


文件结构建议

封装的文件结构

在这里插入图片描述

把二次封装axios所有有关的代码全部放在request文件夹中,其中init.js是二次封装的初始配置,index.js是对init.js再进行一次功能拓展封装,我们真正使用时也是用的这个文件,utils.js是存放相关的工具函数,webConfig.js是一些全局的变量。

为什么这么设计:

  • 代码能够与其他功能解耦
  • 方便直接拷贝整个文件夹放到一个新的项目中直接使用

接口管理文件

在这里插入图片描述
接口都单独放在api文件夹中,并且以后端的业务服务模块进行分类。


二次封装axios的初始配置

init

也就是src/request/init.js文件:

// 配置全局得基础配置
import axios from "axios";// 配置中心
import webConfig from "./webConfig.js"
// 需要的工具函数
import { isCheckTimeout } from "./utils";// 相关库
import router from "@/router";
import store from "@/store";
import { ElMessage } from "element-plus";let request = axios.create({//1,基础配置baseURL: process.env.VUE_APP_BASE_API, // process.env.VUE_APP_BASE_API就是我们在根目录创建对应的环境变量文件里的配置,timeout: 5000, // 设置超时时间为5s
})// 设置请求拦截器
request.interceptors.request.use((config) => {// 1 token添加在请求头中,看看项目的token适合放在localStorage里还是vuex中let token = localStorage.getItem("token");// let token = store.getters.token;// 2 首先有些接口是不需要设置token的,那就不用设置token了let whiteList = webConfig.whiteListApi // 获取不需要设置token的接口白名单let url = config.urlif (whiteList.indexOf(url) === -1 && token) {if (isCheckTimeout()) {// 超时了做登出操作store.dispatch("user/logout");return Promise.reject(new Error("token 失效"));}// 注入token到请求头中config.headers.Authorization = `Bearer ${token}`;}// 3 配置接口国际化config.headers["Accept-Language"] = store.getters.language;// 4 配置响应数据类型config.headers.responseType = "json"return config; // 必须返回配置
}, error => {return Promise.reject(new Error(error))
})// 响应拦截器(处理返回的数据)
request.interceptors.response.use((res) => {//响应得统一处理let { status, message, data } = res.data; // status 这里的状态码是后端定义的,也就是每次请求都是200,但是后端根据情况返回401,500等状态码if (status === 401) {router.push("/noAuth")return Promise.reject(new Error(message)); // 抛出可以自定义错误提示}if (status !== 200) {ElMessage.error("错误码" + status + ":" + message);return Promise.reject(new Error(message));}return res.data; // res 里面其实有很多东西下面截图
}, error => {// 例如断网、跨域、状态码问题的报错ElMessage.error(error.message);// error.response.status 这里可以拿到接口真正的状态码,还是要看后端是怎么设计接口的return Promise.reject(new Error(error));
})export default request;

其中响应数据res为:

在这里插入图片描述
响应失败的error内容有:

在这里插入图片描述

所需其他的文件

utils

utils.js:

/* 处理token的时效性 */// 常量设置
// token 时间戳
export const TIME_STAMP = "timeStamp";
// token超时时长(毫秒) 两小时
export const TOKEN_TIMEOUT_VALUE = 2 * 3600 * 1000;/*** 获取时间戳*/
export function getTimeStamp() {return window.localStorage.getItem(TIME_STAMP);
}
/*** 设置时间戳(就是当前登陆时获取token的时间)*/
export function setTimeStamp() {window.localStorage.setItem(TIME_STAMP, Date.now());
}
/*** 是否超时*/
export function isCheckTimeout() {// 当前时间戳var currentTime = Date.now();// 缓存时间戳var timeStamp = getTimeStamp();return currentTime - timeStamp > TOKEN_TIMEOUT_VALUE;
}

webConfig

webConfig.js:

export default {whiteListApi: ["/a", "/b"],pageSize: [20, 40, 80] // table的页码
}

再封一层

首先要知道为什么还需要进行一次封装,因为单单只对axios做简单的初始化封装还是太过于简陋,未来随着业务的拓展,我们有可能会对接口请求的api做一些功能的拓展。

例如到了某个阶段,我们需要给接口返回的参数添加前端数据缓存。又到了某个阶段,需要阻止接口短时间内重复请求的功能等等。

这时候就很考验我们的代码设计了。

推荐一种链式调用函数的方式去封装,举个例子:

function PromiseFn() { // 用函数封装,因为new Promise就直接执行了return new Promise((resolve, reject) => {// 这里就写异步操作setTimeout(() => {if (true) resolve("图片已经被添加了")else reject("图片没有被添加")}, 1000)});
}function p() {let promise = Promise.resolve()function fn1(result) { // 功能封装1console.log('fn1');return Promise.resolve('fn1')}function fn2(result) { // 功能封装2console.log('fn2');return Promise.resolve('fn2')}let arr = [fn1, fn2]while (arr.length) {promise = promise.then(arr.shift())}return promise
}p('1') // fn1 fn2 轮流执行

好好研究你就会发现,我们是相当于把一个一个功能封装函数当做是一个节点,只需要把传入项经过节点处理后,就得到一个封装过的方法。

按照这个思路我们来试试 index.js 怎么写

import request from "./init";import { ElLoading } from 'element-plus'// 要点 1 把同步的方法也统一以异步调用处理,保证所有方法的顺序 2 可通过配置关闭制定节点处理// 默认配置
//{ 
//     nendCache: false, // 是否直接拿取接口的上一个缓存数据(谨慎开启)
//     nendLoading: true, // 全局加载
//     needRequestingOnlyOne: false, // 是否开启,当前接口在请求时,禁止重复请求的情况(谨慎开启),因为有时候我们需要并发请求
// }let requestApi = (function () {let mapCache = new Map(); // 接口返回的数据缓存let requestingUrl = []; // 正在发送的请求let loadingInstance = null // 全局加载组件return function (config) {let { url, nendCache, nendLoading, needRequestingOnlyOne } = configlet promise = Promise.resolve() // 获取一个成功状态的promise// 节点:全局加载function nodeLoading() {if (nendLoading === undefined || nendLoading === true) {loadingInstance = ElLoading.service({ fullscreen: true })}return Promise.resolve({ keepGoing: true, type: 'then' })}// 节点:是否直接拿取接口的上一个缓存数据function nodeCache() {if (nendCache === true) {// 如果之前已经请求过了,就直接返回缓存的,下面的节点都不用走了ßif (mapCache.has(url)) {if (loadingInstance) loadingInstance.close() // 关闭全局加载return Promise.resolve({ keepGoing: false, type: 'then', data: mapCache.get(url) })} else {return Promise.resolve({ keepGoing: true, type: 'then' })}}return Promise.resolve({ keepGoing: true, type: 'then' })}// 节点:接口还在请求过程中,又发了一次,阻止这种情况function nodeRequestingOnlyOne() {if (needRequestingOnlyOne === undefined || needRequestingOnlyOne === true) {// 看看是不是在请求中if (requestingUrl.indexOf(url) !== -1) {return Promise.resolve({ keepGoing: false, type: 'catch', data: '请求已经提交' })} else {return Promise.resolve({ keepGoing: true, type: 'then' })}}return Promise.resolve({ keepGoing: true, type: 'then' })}// 节点:发起请求async function nodeRequest() {let resData = await request({ ...config }) // 发送请求,等待获取数据return Promise.resolve({ keepGoing: true, type: 'then', data: resData.data })}// 添加新节点// 节点:请求完成后的收尾工作function nodeRequestedFinalDo(result = { keepGoing: true }) {// 需要抛出错误的情况if (result.type === 'catch') {return Promise.reject(result.data)}// 剔除掉正在请求中的接口的标识requestingUrl = requestingUrl.filter(item => {if (item !== url) {return item}})// 写入接口缓存数据mapCache.set(url, result.data)// 关闭全局加载if (loadingInstance) loadingInstance.close()return Promise.resolve(result.data)}let _handleArr = [nodeLoading, nodeCache, nodeRequestingOnlyOne, nodeRequest, nodeRequestedFinalDo] // 需要经过处理的节点function startNodeList() {while (_handleArr.length > 0) {let fn = _handleArr.shift() // 获取当前节点function _final(result = { keepGoing: true }) { // keepGoing 为false表示当前节点不执行,跳到下一个节点if (!result.keepGoing) { // 后面keepGoing为false的节点都走这里return Promise.resolve(result.data || result) // 如果是中途有keepGoing为false的需要取data}// keepGoing为true节点都走这里return fn.call(this, result)}promise = promise.then(_final)}}startNodeList()return promise}
})()
export {requestApi as request, // 进过三次封装的request as initRequest // 不进过第三次封装的
}

这片代码对js的基础要求很高,属于高阶用法了,多看看!


环境配置

之前写init.js的时候,需要配置接口请求基本地址的环境变量。需要在目录中创建:

在这里插入图片描述
然后分别为

# 标志
ENV = 'development'# base api
VUE_APP_BASE_API = '/api'
# 标志
ENV = 'production'# base api
VUE_APP_BASE_API = 'https://mock.mengxuegu.com/mock/625c05ab66abf914b1f1bf10/crm'

使用

ok都写完后就可以使用了,在api/user.js中举例子:

import { initRequest, request } from "../request"// 用二次封装的get请求,参数以对象的形式传入,axios会自动帮我们拼接在请求地址中
export const getUsersAllType = (params) => { return initRequest({url: "/usersAllType",method: "get",params: {...params},})
}// 用三次封装的post请求
export const getUserProfile = (params) => { return request({url: "/profile",method: "post",params: {...params},needRequestingOnlyOne: true // 开启具体某项功能})
}

完成~

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

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

相关文章

【TypeScript】TS中type和interface在类型声明时的区别

🐱 个人主页:不叫猫先生 🙋‍♂️ 作者简介:2022年度博客之星前端领域TOP 2,前端领域优质作者、阿里云专家博主,专注于前端各领域技术,共同学习共同进步,一起加油呀! 💫优质专栏:vue3+vite+typeScript从入门到实践 📢 资料领取:前端进阶资料可以找我免费领取 …

南京邮电大学算法与设计实验二:贪心算法(最全最新,与题目要求一致)

三、实验原理及内容 实验原理: 1、用贪心法实现求两序列的一般背包问题。要求掌握贪心法思想在实际中的应用,分析一般背包的问题特征,选择算法策略并设计具体算法,编程实现贪心选择策略的比较,并输出最优解和最优解值。…

机器学习项目实战-能源利用率 Part-4(模型构建)

博主前期相关的博客可见下: 机器学习项目实战-能源利用率 Part-1(数据清洗) 机器学习项目实战-能源利用率 Part-2(探索性数据分析) 机器学习项目实战-能源利用率 Part-3(特征工程与特征筛选) 这…

【JVM】6. 堆

文章目录 6.1. 堆(Heap)的核心概述6.1.1. 堆内存细分6.1.2. 堆空间内部结构(JDK7)6.1.3. 堆空间内部结构(JDK8) 6.2. 设置堆内存大小与OOM6.2.1. 堆空间大小的设置6.2.2. OutOfMemory举例 6.3. 年轻代与老年…

如何在IDEA中显示时序图

如何在IDEA中显示时序图 有时候,我们想要了解代码的运行过程的时候,想要看到方法的调用流程。 这个时候就希望有一个时序图可以看,但是人工来画时序图太麻烦了,有没有办法在IDEA中直接生成呢? 当然是可以的。 这里…

知识积累(1)

(1) 当您在Git中看到消息 "HEAD is now at 1343ccb FAB-17419 Fix off_chain_data sample error (#146)" 时,这是Git告知您当前所在的分支和最新的提交哈希。 这条消息通常出现在使用Git命令后,如git pull或git check…

【我的创作纪念日】—— 纪念四年的坚持

这是一篇和技术无关的博客,但对我而言,它承载了不菲的价值 普通且宁静的一天,被一条消息戳中,于是,写一篇分享帖,纪念我这 1460 天的坚持初衷: 前言:对过去的回顾 4 年前的我&#…

Python图片转字符画,太好玩啦(46)

小朋友们好,大朋友们好! 我是猫妹,一名爱上Python编程的小学生。 和猫妹学Python,一起趣味学编程。 今日主题 什么是字符画呢? 如何用Python将图片转为字符画? 我们先找一幅原画,比如它吧&…

卡方检验.医学统计实例详解

卡方检验是一种常用的假设检验方法,通常用于分析两个或多个分类变量之间的关系。在医学研究中,卡方检验被广泛应用于分析两种或多种治疗方法的疗效,或者分析某种疾病的发病率与某些危险因素之间的关系。下面我们来看一个卡方检验在医学实例中…

WALN网络不存在? 离谱事件解决方法

前情: 本来在高高兴兴赶ddl,突然发现网断了。尝试重连,显示无法连接... 遂重启,结果事情更糟了,WALN就没了,没了,没了... 这张是修好之后的图,但是当时所有包括蓝牙,waln…

13-2-CSS3-渐变色、2D转换、3D转换

一、渐变色 CSS渐变色(Gradient)是指在元素背景中使用两种或多种不同的颜色进行过渡,超过两个颜色可以形成更为细腻的渐变效果。常见的CSS渐变色有线性渐变和径向渐变。 1. 线性渐变:Linear Gradients 向下/向上/向左/向右/对角…

t检验与Z检验的区别

在统计学中,假设检验是评估某种特定情况下观察到的数据是否符合假设的一种方法。t检验和Z检验是两种常用的假设检验方法,分别用于比较均值差异以及比例差异。在医学统计中,t检验和Z检验经常被用于研究和比较不同治疗方法的效果,例…

基于MAC地址的ACL配置

基于MAC地址的ACL配置 【实验目的】 掌握基于MAC地址的标准ACL的配置。验证配置。 【实验拓扑】 实验拓扑如图1所示。 图1 实验拓扑 设备参数如表所示。 表1 设备参数表 设备 接口 IP地址 子网掩码 默认网关 S1 e0/0 N/A N/A N/A e0/1 N/A N/A N/A PC1 N/…

IntelliJ 导出和导入配置文件

导出: File->Manage IDE Setting->Export Settings…->选择导出目录,可以得到一个 settings.zip 的文件 导入方法: File->Manage IDE Setting->Import Settings…->选择需要导入的文件即可,导入之后重启IDE。

chatgpt赋能Python-pythonfind

Python文件搜索工具Pythonfind 在开发过程中,文件搜索工具是一个非常重要的工具。在大型项目中,可能需要查找特定类型的文件或者在代码库中查找特定的代码块。 Pythonfind是一个非常强大和灵活的python文件搜索工具,可以帮助我们简化这个过程…

Go 语言核心编程-环境入门篇

第 1 章 Golang 开山篇 1.1 Golang 的学习方向 Go 语言,我们可以简单的写成 Golang 1.2 Golang 的应用领域 1.2.1区块链的应用开发 1.2.2后台的服务应用 1.2.3云计算/云服务后台应用 1.3 学习方法的介绍 1.4 讲课的方式的说明 努力做到通俗易懂注重 Go 语言体系&…

云从科技从容大模型:大模型和AI平台什么关系?为什么造行业大模型?

原创:亲爱的数据 2023年5月18日,坐标广州南沙,来自云从科技的“云从从容大模型”正式亮相。 自此,云从科技从CV四小龙“进阶”成为一家AI大模型公司,同时,别忘记云从还有一个身份常被人提起:首家…

python 文件操作 , 异常处理 , 模块和包

文件操作 1.写数据 # open(name, mode) # name:是要打开的目标文件名的字符串(可以包含文件所在的具体路径)。 # mode:设置打开文件的模式(访问模式):只读、写入、追加等。 #1.打开文件---通道建立--申请资源 # w 模式会清空之前的内…

C++13-STL模板

C13-STL模板 在线练习: http://noi.openjudge.cn/ https://www.luogu.com.cn/ 大纲要求 【 3 】算法模板库中的函数:min、max、swap、sort 【 4 】栈 (stack)、队列 (queue)、链表 (list)、 向量(vector)等容器 1.函数模板 泛…

GPT专业应用:生成会议通知

正文共 917 字,阅读大约需要 3 分钟 公务员/文秘必备技巧,您将在3分钟后获得以下超能力: 快速生成会议通知 Beezy评级 :B级 *经过简单的寻找, 大部分人能立刻掌握。主要节省时间。 推荐人 | Kim 编辑者 | Linda ●图…