记录--axios和loading不得不说的故事
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
loading的展示和取消可以说是每个前端对接口的时候都要关心的一个问题。这篇文章将要帮你解决的就是如何结合axios更加简洁的处理loading展示与取消的逻辑。
首先在我们平时处理业务的时候loading一般分为三种:按钮loading,局部loading,还有全局loading。
按钮loading
其实想写这篇博客的诱因也是因为这个按钮loading ,在大多数时候我们写按钮loading业务的时候是这样写的。
const loading = ref(false)
try {
loading.value = true
const 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的开始与结束状态。而且好多接口都要这么写。这样太繁琐了,那我们可不可以这样呢?
vue3版本
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版本
在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可以取任意名字。但是里面必须有value
loading: { value: false },
}
},
mounted () {
const data = await post(`/api/data`,{loading:this.loading})
},
}
</script>
可以看到实现的原理也很简单。我们在axios里面把出传入的config中的loading对象也变成一个实例对象就好了。在实例对象中记录我们传入的对象,也是以为这里我们会比vue3的写法多一个loading,从而实现响应式。
高阶函数版本
以上的方案看起来还是很不友好。如果我们不拘泥于在拦截器中封装呢?
axios代码如下:
const _axios = axios.create({
method: `post`,
baseURL: import.meta.env.VITE_BASE_URL,
})
// 请求拦截器
_axios.interceptors.request.use()
// 响应拦截器
_axios.interceptors.response.use()
async setRequest (callBack, url, params, config) {
//添加按钮的loading
if (config.loading) {
config.loading.value = true
}
try {
return await callBack(url, params, config)
}
catch (error) {
return Promise.reject(error)
}
finally {
//关闭按钮的loading状态
if (config.loading) {
config.loading.value = false
}
}
}
export const post = (url, params, config) => {
return setRequest(_axios.post,url, params, config)
}
以上代码仅仅是个代码思路。我们可以使用高阶函数二次封装 axios的post函数。get等函数也是一样的,这里就不一一举例了。
局部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.dom
if (loadingTarget === undefined) return
const 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.dom
if (loadingTarget === undefined) return
const 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 = true
dom.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即可!
本文转载于:
https://juejin.cn/post/7215424335719923772
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
记录--axios和loading不得不说的故事的更多相关文章
- 与《YII框架》不得不说的故事—5篇目录
与<YII框架>不得不说的故事—基础篇 第1章 课程目标 1-1 课程目标 (00:54) 第2章 课前知识准备 2-1 YII的启动和安装 (05:12) 2-2 YII请求处理流程 ( ...
- 【征文】Hadoop十周年特别策划——我与Hadoop不得不说的故事
2016年是Hadoop的十周年生日,在今年,CSDN将以技术和实战为主题与大家共同为Hadoop庆生.其主要内容包含Hadoop专业词典.系列视频技术解析.Hadoop行业实践.线上问答.线下沙龙. ...
- asList和ArrayList不得不说的故事
目录 简介 创建ArrayList UnsupportedOperationException asList 转换 总结 asList和ArrayList不得不说的故事 简介 提到集合类,ArrayL ...
- 我与PHP,ULM和Vue.js不得不说的故事(一个放荡不羁与一个神神秘秘一个似曾相识,从入门到放弃记录第二章)
·关于UML(git) 究竟是命运在茫茫语言之中遇到了你,还是我的魅力让你向我奔涌而来.好吧都不是,我俩就像古代包办婚姻,被专业牢牢的绑在一起了,既然都是一条绳上的蚂蚱.我我们应该能体谅彼此的不容易, ...
- 我与PHP和git不得不说的故事(梦开始的地方,从入门到放弃记录第一章)
·关于下载 阿瑶瑶跟wampsever官网搏斗了一下午,其实我觉得教材可能在PUA我.谷歌说它给的网址安全证书过期,然后下载以断网收场.(阿瑶的第一战,以失败告终) [经过我玲姐指点,下载路径变为迅雷 ...
- Visual Studio 20周年,我和VS不得不说的故事(内含福利)
Visual Studio 2017正式版已如期发布(点击这里查看发布全记录)!自去年 11 月正式宣布 Visual Studio 项目之后,微软终于正式推出了“宇宙最强集成开发环境(IDE)”的最 ...
- Visual Studio 20周年,我和VS不得不说的故事
Visual Studio 2017正式版已如期发布(点击这里查看发布全记录)!自去年 11 月正式宣布 Visual Studio 项目之后,微软终于正式推出了“宇宙最强集成开发环境(IDE)”的最 ...
- 和java面试不得不说的故事
一直都没有想到,可以有机会可以面试他人,很感谢现在的公司,给我不少的机会可以尝试从不同方面尝试一些工作,在入职现公司之前也作为面试者参加过不少面试,不过还好,面试通过率都还好,大部分是待遇谈不拢.现在 ...
- 记录axios在IOS上不能发送的问题
最近 遇到 了axios在IOS上无法发送的问题,测试 了两个 苹果 机,IOS10上不能发送,IOS12可以,百度了下,找到了解决方法.记录下吧 首先引入qs,这个安装axios也已经有了吧:然后在 ...
- 微信、QQ和手机号之间不得不说的故事!
发文字,发图片,发心情,视频聊天,查看附近的人,微信能干的事情QQ都可以,那么它们有什么区别,我QQ用得好好的为什么要我联系人都导到微信去?我们很早就有了QQ,但是在QQ时代,我们虽然用QQ发消息聊天 ...
随机推荐
- 【Android】使用MediaExtractor、MediaMuxer去掉视频文件中的音频数据
1 简介 本文以 mp4 文件为例,讲解去音频操作.mp4 是一种视频封装的容器,里面包含音频(audio)和视频(video)数据,对应的数据编码格式分别为 aac 和 h264.在去音频过程中 ...
- spring boot读取json文件并实现接口查询
0.源码下载 https://download.csdn.net/download/IndexMan/84238085 1.说明 最近需要在spring boot项目启动时读取json文件并保存到Li ...
- dp题单——区间dp
一.基本概念 1.链式区间dp for(int len = 2; len <= n; len++){ //枚举区间长度 for(int i = 1; i + len - 1 <= n; i ...
- Golang从入门到跑路-从基础到微服务学习路线图
收录的awesome-go项目,学习基础系列,go项目实战,go源码分析,go开发者成长路线图等等,把他们收集起来学习. 地址:https://github.com/jiujuan/go-collec ...
- 【Android 逆向】【攻防世界】ill-intentions
1. apk 安装到手机, 啥输入框都没有 2. apk拖入到jadx中看看 public class MainActivity extends Activity { @Override // and ...
- android 逆向笔记
壳检测工具 GDA 2. 逆向分析APP 一般流程 1. 使用自动化检测工具检测APP是否加壳,或者借助一些反编译工具依靠经验判断是否加壳 2. 如果apk加壳,则需要先对apk进行脱壳 3. 使用` ...
- 如何保证消息顺序执行(Rabbitmq/kafka)
转载: https://www.cnblogs.com/-wenli/p/13047059.html https://www.jianshu.com/p/02fdcb9e8784
- 正则计算器---day19
计算下面表达式最后的结果 strvar = "1-2*((60-30+(-40/5)*(9-2*5/3+7/3*99/4*2998+10*568/14))-(-4*3)/(16-3*2))& ...
- Java新建一个子线程异步运行方法
如何在运行主方法的同时异步运行另一个方法,我是用来更新缓存: 1. 工具类 public class ThreadPoolUtils { private static final Logger LOG ...
- 使用ConfuserEx代码混淆工具保护你的.NET应用程序
前言 .NET应用如何防止被反编译?这个对于我们.NET开发而言是一个值得关注和重视的问题,防止应用程序被反编译的手段有很多本文我们主要讲讲如何使用ConfuserEx .NET开源免费的代码混淆工具 ...