如何在 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命…...

深入理解Java ThreadLocal及其内存泄漏防范
文章目录 一、ThreadLocal简介二、ThreadLocal的内存泄漏问题三、防止ThreadLocal导致的内存泄漏四、总结 一、ThreadLocal简介 在Java中,ThreadLocal是一种线程封闭的机制,其主要目的是为每个线程都创建一个单独的变量副本。这意味着,每个线…...

介绍10款ChatGPT替代产品
ChatGPT 引领着聊天 AI 的世界,许多人已经开始在日常生活中使用它。OpenAI 的 GPT-3 语言模型是聊天机器人的基础,它使得用户能够通过回答问题与 AI 进行交互。 GPT-4 的引入为机器人提供了更强大的功能。然而,它也有一个明显的缺点ÿ…...

数字逻辑 期末
概述 教材:《电子技术基础(数字部分)》 第六版 7400系列是TTL型芯片,商用型 数制 十进制->二进制 除2取余法&乘2取整法(注意精度,但计科简单不考) 十六进制->二进制 一位变四位 八…...

MT4交易外汇平台有哪些优势?为何是外汇投资首选?
外汇市场上存在着各种各样的外汇交易商,但是很多的外汇交易商所选择的交易平台都是MT4交易外汇平台。作为全世界范围内使用最为广泛的交易平台,MT4交易外汇平台具有哪些优势,能够让外汇交易商和外汇投资者都选择使用。本文就来具体的聊聊&…...

问卷调查工具实力榜单发布
问卷调查是从目标受众那里收集有价值的反馈和见解的有效方式。正确的调查问卷工具可以使问卷的创建、分发和分析变得更加容易和高效。在本文中,我们将问卷调查工具排行榜实力榜,为大家选择问卷平台的时候提供有价值的参考意见。 1、Zoho Survey Zoho S…...

javascript中property和attribute有什么区别?
在JavaScript中,“property”(属性)和"attribute"(属性)这两个术语用于描述对象的特性,但它们在含义和用法上有一些区别。 1、属性(Properties): 属性是属于J…...

快速上手kettle
一、前言 最近由于工作需要,需要用到kettle工具进行数据迁移转换。特意找资料学习了一下,kettle基本操作算是学会了。 所学的也结合实际工作进行了验证。为了防止以后用到忘记了,便写了几篇文章记录一下。 二 、ETL简介 ETL ( Extract-Tran…...

Leetcode 399. 除法求值
Leetcode 399. 除法求值题目 给你一个变量对数组 equations 和一个实数值数组 values 作为已知条件,其中 equations[i] [Ai, Bi] 和 values[i] 共同表示等式 Ai / Bi values[i] 。每个Ai 或 Bi 是一个表示单个变量的字符串。另有一些以数组 queries 表示的问题&am…...

kotlin协程并发/并行与串行互相切换,CoroutineScope与await
kotlin协程并发/并行与串行互相切换,CoroutineScope与await import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.time.LocalTimefun main(args: Arra…...

初识linux之简单了解TCP协议与UDP协议
目录 一、理解源IP地址和目的IP地址 二、端口号 1. 为什么要有端口号 2. 理解端口号 3. 源端口号和目的端口号 三、初步了解TCP协议和UDP协议 1. 初步认识TCP协议 2. 初步认识UDP协议 3. 可靠传输与不可靠传输 四、网络字节序 1. 网络字节序的概念 2. 如何形成网络…...