如何在 Axios 中去控制 Loading?大有学问!
目录
前言
按钮loading
局部loading
全局loading
前言
loading 的展示和取消可以说是每个前端对接口的时候都要关心的一个问题。这篇文章将要帮你解决的就是如何结合axios更加简洁的处理loading展示与取消的逻辑。
首先在我们平时处理业务的时候loading一般分为三种:按钮loading,局部loading,还有全局loading。
按钮loading
其实想写这篇博客的诱因也是因为这个按钮 loading ,在大多数时候我们写按钮 loading 业务的时候是这样写的。
const loading = ref(false)
try {loading.value = trueconst data = await axios.post(`/api/data`)
}
finally {loading.value = false
}
或者这样的写的
const loading = ref(false)
loading.value = true
axios.post(`/api/data`).then(data => {//do something}).finally(() => {loading.value = false})
可以看到 我们总要处理loading的开始与结束状态。而且好多接口都要这么写。这样太繁琐了,那我们可不可以这样呢?
const loading = ref(false)
const data = await axios.post(`/api/data`,{loading:loading})
把loading的状态给axios统一处理。这样代码是不是就简洁多了呢?处理方式也很简单。
// 请求拦截器
axios.interceptors.request.use(config = >{if (config.loading) {config.loading.value = true}
})
// 响应拦截器
axios.interceptors.response.use(response => {if (response.config.loading) {res.config.loading.value = false}},error => {if (error.config.loading) {config.loading.value = false}}
)
我们只需要在axios的拦截器中改变loading的值就可以,注意一定要传入一个ref类的值。这种写法也仅适用于vue3。vue2是不行的。
在vue2里面我们可能会想到这样写。
<template><a-button loading="loading.value">保存</a-button>
</template><script>export default {data () {return {loading: { value: false },}},mounted () {const data = await axios.post(`/api/data`,{loading:this.loading})},}
</script>
//拦截器和vue3写法一样
但是很遗憾这样是无法生效的。原因如下
//接口调用
axios.post(接口地址,配置项)
//拦截器
axios.interceptors.request.use(配置项 => {})
在axios中我们接口调用传入的配置项 和 拦截器返回的配置项 并不是同一个内存地址。axios做了深拷贝处理。所以传入的loading对象和返回的loading对象并不是同一个对象。所以我们在拦截器中修改是完全没有用的。
可是vue3为什么可以呢?因为 ref 返回的对象是 RefImpl 类的实例,并不是一个普通的对象,axios在做深拷贝的时候没有处理这种实例对象。所以我们就可以从这里出发来改造一下我们的axios写法。代码如下:
axios代码:
const _axios = axios.create({method: `post`,baseURL: process.env.VUE_APP_BASE_URL,
})
//注意:拦截器中比vue3多了个loading!!!
// 请求拦截器
_axios.interceptors.request.use(config = >{if (config.loading) {config.loading.loading.value = true}
})
// 响应拦截器
_axios.interceptors.response.use(response => {if (response.config.loading) {res.config.loading.loading.value = false}},error => {if (error.config.loading) {config.loading.loading.value = false}}
)export const post = (url, params, config) => { if (config?.loading) {class Loading {loading = config.loading}config.loading = new Loading()}return _axios.post(url, params, config)
}
使用方式:
<template><a-button loading="loading.value">保存</a-button>
</template><script>import { post } from '@api/axios'export default {data () {return {//这里的loading可以取任意名字。但是里面必须有valueloading: { value: false },}},mounted () {const data = await post(`/api/data`,{loading:this.loading})},}
</script>
可以看到实现的原理也很简单。我们在axios里面把出传入的config中的loading对象也变成一个实例对象就好了。在实例对象中记录我们传入的对象,也是以为这里我们会比vue3的写法多一个loading,从而实现响应式。
局部loading
局部loading的添加有两种方式:
-
·使用自定义指令 传入true和false 。这样的缺陷是不够灵活,组件内的元素就很难局部添加了,只能全组件添加。值得一提的是,改变true和false的逻辑就可以用我们上述的按钮loading方法。具体的实现方式这里就不再讲述了,如果需要的话可以评论区留言。
-
·在axios中封装。每次调用接口的时候传入需要添加loading的dom。接口调用完毕删除dom。实现方法如下。
这里是vue3 + antdV3 技术栈的一个封装。这里用hooks把设置删除loading的逻辑给拆了出去。
axios代码:
const _axios = axios.create({method: `post`,baseURL: import.meta.env.VITE_BASE_URL,
})const { setLoading, deleteLoading } = useAxiosConfig()
// 请求拦截器
_axios.interceptors.request.use(config = >{setLoading(config)
})
// 响应拦截器
_axios.interceptors.response.use(response => {deleteLoading(res.config)},error => {deleteLoading(res.config)}
)export const post = (url, params, config) => { return _axios.post(url, params, config)
}
hooks代码
import { createApp } from 'vue'
import QSpin from '@/components/qSpin/QSpin.vue'
import type { RequestConfig, AxiosError } from '@/types/services/http'
export default function () {/** 使用WeakMap类型的数据 键名所指向的对象可以被垃圾回收 避免dom对象的键名内存泄漏 */const loadingDom = new WeakMap()/*** 添加局部loading* @param config*/const setLoading = (config: RequestConfig) => {const loadingTarget = config.domif (loadingTarget === undefined) returnconst loadingDomInfo = loadingDom.get(loadingTarget)if (loadingDomInfo) {loadingDomInfo.count++} else {const appExample = createApp(QSpin)const loadingExample = appExample.mount(document.createElement(`div`)) as InstanceType<typeof QSpin>loadingTarget.appendChild(loadingExample.$el)loadingExample.show(loadingTarget)loadingDom.set(loadingTarget, {count: 1, //记录当前dom的loading次数appExample,})}}/*** 删除局部loading* @param config*/const deleteLoading = (config: RequestConfig) => {const loadingTarget = config.domif (loadingTarget === undefined) returnconst loadingDomInfo = loadingDom.get(loadingTarget)if (loadingDomInfo) {if (--loadingDomInfo.count === 0) {loadingDom.delete(loadingTarget)loadingDomInfo.appExample.unmount()}}}return { setLoading, deleteLoading }
}
基础逻辑,很简单。只需要接口请求的时候的添加loading ,接口响应完成的时候删除loading。但是随之而来的就有一个问题,如果多个接口同时请求 或者 一个接口频繁请求需要覆盖的都是同一个dom,这样我们添加的loading就会有很多个相同的,相互覆盖。因此上述代码定义了一个loadingDom 记录当前正在loading的dom有哪些,如果有一样的进来的 就把count加一 ,结束后就把count减一。如果count为零则删除loading。
使用实例代码:
<template><div><div ref="head_dom">我是头部数据</div><a-card ref="card_dom">我是卡片内容</a-card></div>
</template><script setup lang="ts">import { post } from '@api/axios'import { ref, onMounted } from 'vue'const head_dom = ref()const card_dom = ref()//这边写了两个是为了演示下 直接在html标签上面绑定ref拿到的就是dom。在组件上面拿到的是组件实例要$el一下onMounted(async () => {const data1 = await post(`/api/head`, { dom: head_dom.value })const data2 = await post(`/api/card`, { dom: card_dom.value.$el })})
</script>
下面简单解释下hooks代码中QSpin组件的代码。
<template><div v-show="visible" class="q-spin"><spin tip="加载中" /></div>
</template><script setup lang="ts">import { Spin } from 'ant-design-vue'import { ref } from 'vue'const visible = ref(false)const show = (dom: HTMLElement) => {visible.value = truedom.style.transform = dom.style.transform || `translate(0)`}defineExpose({ show })
</script><style scoped lang="less">.q-spin {position: fixed;z-index: 999;top: 0;bottom: 0;left: 0;right: 0;display: flex;flex-direction: column;justify-content: center;align-items: center;background-color: rgb(0 0 0 / 10%);}
</style>
这里是对antdv3的Spin组件做了一个简单的二次封装。主要讲解的就是一个loading覆盖传入dom的方法。
大多数地方使用的方式都是 relative 和 absolute 定位组合的方式,但是这里采用了transform 和 fixed定位组合的方式。因为我们的项目中可能出现这样一种情况
<div style="position: relative"><div ref="div_dom"><div style="position: absolute">我是内容</div></div></div>
假如 我们要给中间的的div添加loading, 使用relative 和 absolute 定位组合的方式。那么中间的div就会在样式表种添加一个position: relative的属性,这样代码就会变成这样
<div style="position: relative"><div style="position: relative" ref="div_dom"><div style="position: absolute">我是内容</div></div></div>
很明显 我们第三层div定位的根节点就从第一层变成了第二层,这样就会有可能导致我们样式的错乱。因此笔者采用了transform 和 fixed定位组合的方式。虽然上述的情况可能还会出现 但是会大大减少出现的可能性。
全局loading
这个就很简单了。如果你封装好了局部的loading 直接在配置项的dom中传入document.body 即可!
相关文章:
如何在 Axios 中去控制 Loading?大有学问!
目录 前言 按钮loading 局部loading 全局loading 前言 loading 的展示和取消可以说是每个前端对接口的时候都要关心的一个问题。这篇文章将要帮你解决的就是如何结合axios更加简洁的处理loading展示与取消的逻辑。 首先在我们平时处理业务的时候loading一般分为三种&#x…...

充电桩检测设备厂家TK4860C交流充电桩检定装置
TK4860系列是专门针对现有交流充电桩现场检测过程中接线复杂、负载笨重、现场检测效率低等问题而研制的一系列高效检测仪器,旨在更好的开展充电桩的强制检定工作。 充电桩检测设备是一款在交流充电桩充电过程中实时检测充电电量的标准仪器,仪器以新能源…...

一文3000字实现基于Selenium+Python的web自动化测试框架
一、什么是Selenium? Selenium是一个基于浏览器的自动化测试工具,它提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。Selenium主要包括三部分:Selenium IDE、Selenium WebDriver 和Selenium Grid。 Selenium IDE:Firefo…...

Android 12系统源码_窗口管理(二)WindowManager对窗口的管理过程
前言 上一篇我们具体分析了窗口管理者WindowManagerService的启动流程,对于WindowManagerService有了一个初步的认识。在此基础上,我本打算应该进一步分析WindowManagerService是如何管理系统中的各种窗口的,然而由于Android系统的架构设计,在分析WindowManagerService之前…...

python3.8,torch1.10.2+cu113、torch-geometric 安装
【1】conda create -n name python=3.8 【2】安装 torch 注意先看可适应的最高cuda版本 https://data.pyg.org/whl/ 版本对应 【3】按照顺序安装torch-geometric: torch-sparse、torch-scatter、torch-cluster、 torch-spline-conv \torch-geometric pip install torc…...
堆(heap)、栈(stack)
在程序中,栈和堆是两种非常重要的数据结构。它们都用来存储数据,但是它们的定义略有不同。 栈Stack: 栈是一种线性的数据结构,它以 “后进先出”(LIFO)的方式存储数据。栈中的内存空间在编译时就已经确定,大…...

企业级API网关之典型应用场景
目 录 01 企业面对API与网关的现状 02 APIGW介绍及企业应用场景 03 总结 01 企业面对API与网关的现状 在企业中,进行新的系统/应用/产品开发时,具有周密的流程:从需求分析、设计、开发、测试、发布与验收。所以,一…...

【2023年4月美赛加赛】Z题:The future of Olympics 25页完整论文
【2023年4月美赛加赛】Z题:The future of Olympics 25页完整论文 1 题目 背景 国际奥委会(IOC)正面临着夏季奥运会和冬季奥运会申办数量的减少**[1]**。在过去,举办奥运会的竞争非常激烈,声望也很高。然而,最近,主办…...
Rocket重试机制,消息模式,刷盘方式
一、Consumer 批量消费(推模式) Consumer端先启动 Consumer端后启动. 正常情况下:应该是Consumer需要先启动 consumer.setConsumeMessageBatchMaxSize(10);//每次拉取10条 package quickstart; import java.util.List; import co…...

linux+onenet可视化(图形化步骤)
文章目录 一、ONENET项目搭建1.1 ONENET注册1.2 创建产品与设备1.3 添加数据流 二、可视化配置 OneNET是由中国移动打造的PaaS物联网开放平台。平台能够帮助开发者轻松实现设备接入与设备连接,快速完成产品开发部署,为智能硬件、智能家居产品提供完善的物…...
汇编的基础
原视频 基础篇:1.1编程环境的安装 打开DOSBox 0.74-3 Options.bat调整窗口大小 windowresolution1200x640 outputddrawmount c D:\masm c: debugDEBUG 用Debug的R命令查看、改变CPU寄存器的内容: 用Debug的D命令查看内存中的内容: 用Debu…...

并发编程学习(十四):tomcat线程池
1、Tomcat 功能组件结构 Tomcat 的核心功能有两个,分别是负责接收和反馈外部请求的连接器 Connector,和负责处理请求的容器 Container。 其中连接器和容器相辅相成,一起构成了基本的 web 服务 Service。每个 Tomcat 服务器可以管理多个 Servi…...

简洁灵活工单管理系统,支持工单模版字段、工单状态自定义
一、开源项目简介 本项目为FeelDesk工单管理系统的开源版(OS),是基于开发者版(DEV)分离的标准版;支持工单模版字段、工单状态等自定义,可为不同的模版设置不同的路由规则;对工单需求…...

标签派单系统架构设计
需求描述 项目背景 根据员工历史成单情况,计算员工对不同类型工单的转化能力。根据员工和工单标签匹配进行派单。 业务流程图 规则描述 每10分钟,分城进行一次派单,派单规则可能会动态删减,需要支持动态配置 工单标签说明 一…...

Jmeter和Postman那个工具更适合做接口测试?
软件测试行业做功能测试和接口测试的人相对比较多。在测试工作中,有高手,自然也会有小白,但有一点我们无法否认,就是每一个高手都是从小白开始的,所以今天我们就来谈谈一大部分人在做的接口测试,小白变高手…...

k8s污点与容忍
1.前言 污点是给node节点打上污点标签,使得pod不能往该node节点上调度,污点有三种模式,分别是NoSchedule、PreferNoSchedule、NoExecute,容忍是给pod打上和node节点一样的污点标签,使pod能调度到带有该污点标签的node…...
市面上有哪些软件可以结合agentgpt的?众包平台结合的好处!
使用AgentGPT,提升工作效率! 随着科技的迅速发展,人工智能已经成为我们生活中不可或缺的一部分。而AgentGPT则是人工智能领域的一款杰出产品,它能够帮助我们提升工作效率,减少重复性劳动,让我们的生活更加便…...

【js】对象属性的拦截和Proxy代理与Reflect映射的用法与区别
✍️ 作者简介: 前端新手学习中。 💂 作者主页: 作者主页查看更多前端教学 🎓 专栏分享:css重难点教学 Node.js教学 从头开始学习 ajax学习 文章目录 对象属性的拦截介绍SetGet 对象的拦截介绍使用对象属性拦截和对象拦截区别练习题 映射…...

Yolov8涨点神器:ODConv+ConvNeXt提升小目标检测能力
1.涨点神器结合,助力YOLO 1.1 ICLR 2022涨点神器——即插即用的动态卷积ODConv 论文:Omni-Dimensional Dynamic Convolution 论文地址:Omni-Dimensional Dynamic Convolution | OpenReview ODConv通过并行策略引入一种多维注意力机制以对卷积核空间的四个维度学习更灵活的…...

git代码回滚是使用reset还是revert
时光不能回退,Git却允许我们改变历史。 想要让Git回退历史,有以下步骤: 使用git log命令,查看分支提交历史,确认需要回退的版本 使用git reset --hard commit_id命令,进行版本回退 使用git push origin命…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...

CTF show Web 红包题第六弹
提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框,很难让人不联想到SQL注入,但提示都说了不是SQL注入,所以就不往这方面想了 先查看一下网页源码,发现一段JavaScript代码,有一个关键类ctfs…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

高等数学(下)题型笔记(八)空间解析几何与向量代数
目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...