记录--axios和loading不得不说的故事

news2024/12/27 1:34:48

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助

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的添加有两种方式:

  1. 使用自定义指令 传入true和false 。这样的缺陷是不够灵活,组件内的元素就很难局部添加了, 只能全组件添加。值得一提的是,改变true和false的逻辑就可以用我们上述的按钮loading方法。具体的实现方式这里就不再讲述了,如果需要的话可以评论区留言。
  2. 在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

如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。

 

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

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

相关文章

OpenAI 重磅发布 ChatGPT iOS 客户端!

公众号关注 “GitHubDaily” 设为 “星标”&#xff0c;每天带你逛 GitHub&#xff01; 今天凌晨&#xff0c;OpenAI 正式发布了 iOS 客户端&#xff01; 这代表你可以直接在 iPhone 和 iPad 上直接使用 ChatGPT 进行聊天了。 该客户端基于 Whisper 开源模型&#xff0c;集成了…

用排列组合来编码通信(七)——《我的5/4张牌的预言》

早点关注我&#xff0c;精彩不错过&#xff01; 从《5张牌的预言》开始&#xff0c;前面介绍了3个拓展思路&#xff0c;分别从引入额外信息解放选牌&#xff08;Eigens value&#xff09;&#xff0c;引入正反信息来编码&#xff08;ups and downs&#xff09;&#xff0c;继续…

从零开始学架构——可扩展架构模式

可扩展架构模式的基本思想和模式 软件系统与硬件和建筑系统最大的差异在于软件是可扩展的&#xff0c;一个硬件生产出来后就不会再进行改变、一个建筑完工后也不会再改变其整体结构 例如&#xff0c;一颗 CPU 生产出来后装到一台 PC 机上&#xff0c;不会再返回工厂进行加工以…

从零玩转前后端加解密之SM2-sm2

title: 从零玩转前后端加解密之SM2 date: 2022-08-21 19:42:00.907 updated: 2023-03-30 13:28:48.866 url: https://www.yby6.com/archives/sm2 categories: - 加密算法 - 从零玩转系列 tags: - 加密算法 - sm2 前言 SM2是国家密码管理局于2010年12月17日发布的椭圆曲线公钥…

工业互联网UWB定位系统源码,支持自定义开发

工厂人员定位系统&#xff0c;采用UWB定位技术&#xff0c;通过在厂区内部署一定数量的定位基站&#xff0c;以及为人员、车辆、物资佩戴标签卡的形式&#xff0c;实时获取人员精确位置&#xff0c;精度高达10cm。 文末获取联系 工厂人员定位系统可实现物资/车辆实时定位&#…

不同厂家对讲机耳塞耳挂/领夹型988对讲机如何写频改频点/频率能互相通信

988型号都是很多厂家代工出来的,代工出来默认的频点都不一样,有可能买回来的2个不同厂家生产的对讲机,这样它们要能通讯,必须要同频点才能互通,它一般出厂设定16个频道,长按+和-键来切换频道。 需要用到typeC 的写频线,其实是用CH430芯片的usb写频线,可以找厂家要写频线…

编程语言中,循环变量通常都用 i?你知道为什么吗?

01 前天&#xff0c;我在朋友圈发了一个问题&#xff1a; 为什么编程中&#xff0c;循环变量通常都是用 i ? 没想到&#xff0c;回复的人这么多&#xff01;要连翻好几页。 这个问题&#xff0c;有 2/3 的人回答正确&#xff0c;有少部分人知道&#xff0c;但是不太确定。 习惯…

Hadoop基础学习---2、Hadoop概述

1、Hadoop概述 1.1 Hadoop是什么&#xff1f; 1、Hadoop是一个又Apache基金会所开发的分布式系统基础架构。 2、主要解决海量数据的存储和海量数据的分析计算。 3、广义上来说&#xff0c;Hadoop通常是指一个更广泛的概念——Hadoop生态圈。 1.2 Hadoop 优势&#xff08;4高…

六级备考28天|CET-6|翻译井冈山|2021年12月|8:20~9:40+ ~10:17

目录 四级翻译5篇必练真题 六级翻译5篇必练真题 井冈山 四级翻译5篇必练真题 ①2023年3月一卷①自驾游 ②2022年 12月一卷③立秋 ③2022年6月一卷①拔苗助长 ④2021年 12月一卷②大运河 ⑤2021年6月一卷③普洱茶 六级翻译5篇必练真题 ①2023年3月一卷②郑和下西洋 ②2022年…

微服务多模块:Springboot+Security+Redis+Gateway+OpenFeign+Nacos+JWT (附源码)仅需一招,520彻底拿捏你

可能有些人会觉得这篇似曾相识&#xff0c;没错&#xff0c;这篇是由原文章进行二次开发的。 前阵子有些事情&#xff0c;但最近看到评论区说原文章最后实现的是单模块的验证&#xff0c;由于过去太久也懒得验证&#xff0c;所以重新写了一个完整的可以跑得动的一个。 OK&#…

nvidia-smi 失效解决

服务器重启后&#xff0c;跑模型发现&#xff1a; RuntimeError: Attempting to deserialize object on a CUDA device but torch.cuda.is_available() is False. If you are running on a CPU-only machine, please use torch.load with map_locationtorch.device(cpu) to ma…

Linux常用命令——hping3命令

在线Linux命令查询工具 hping3 测试网络及主机的安全 补充说明 hping是用于生成和解析TCPIP协议数据包的开源工具。创作者是Salvatore Sanfilippo。目前最新版是hping3&#xff0c;支持使用tcl脚本自动化地调用其API。hping是安全审计、防火墙测试等工作的标配工具。hping优…

【开源项目】Nepxion Aquarius实现分布式锁、缓存、ID生成、限速的源码解读

项目地址 项目地址 https://toscode.gitee.com/nepxion/Aquarius 项目介绍 Nepxion Aquarius是一款基于Redis Zookeeper的分布式应用组件集合&#xff0c;包含分布式锁&#xff0c;缓存&#xff0c;ID生成器&#xff0c;限速限流器。它采用Nepxion Matrix AOP框架进行切面架构…

天工开物 #6 Git 分支管理与版本发布

Git 版本管理系统由 Linux 的作者 Linus Torvalds 于 2005 年创造&#xff0c;至今不到二十年。 起初&#xff0c;Git 用于 Linux Kernel 的协同开发&#xff0c;用于替代不再提供免费许可的 BitKeeper 软件。随后&#xff0c;这一提供轻量级分支的分布式版本管理系统得到了开源…

产品经理被气的脸都绿了!

见字如面&#xff0c;我是军哥&#xff01; 本来今天不想发文了&#xff0c;想躺平一下&#xff0c;毕竟今天周五了嘛。 可是今天早上一位买了我《技术人核心能力》的程序员学员发来私信&#xff0c;说他给产品经理上了一课&#xff0c;声称产品经理当时脸都绿了&#xff0c;并…

浅浅的理解MVI

MVI 的概念 官网解释&#xff1a; https://developer.android.google.cn/topic/architecture?hlzh-cn MVI在架构分层上和MVP没有本质区别&#xff0c;但区别主要体现在架构风格和编程思想上。 MVI 是 Model-View-Intent 的缩写&#xff0c;它也是一种响应式 流式处理思想的…

【Linux高级 I/O(2)】如何使用阻塞 I/O 与非阻塞 I/O?——select()函数

上次我们虽然使用非阻塞式 I/O 解决了阻塞式 I/O 情况下并发读取文件所出现的问题&#xff0c;但依然不够完美&#xff0c;使得程序的 CPU 占用率特别高。解决这个问题&#xff0c;就要用到本文将要介绍的 I/O 多路复用方法。 何为 I/O 多路复用 I/O 多路复用&#xff08;IO m…

数据结构-时间复杂度和空间复杂度

时间复杂度和空间复杂度 算法效率时间复杂度空间复杂度表示方法&#xff08;大O的线性表示&#xff09;举例说明时间复杂度举例说明空间复杂度举例说明冒泡排序的时间和空间复杂度递归情况下的时间和空间复杂度两个例子 算法效率 算法&#xff08;Algorithn&#xff09;是指用来…

Spring MVC的核心类和注解

DispatcherServlet DispatcherServlet作用 DispatcherServlet是Spring MVC的核心类&#xff0c;也是Spring MVC的流程控制中心&#xff0c;也称为Spring MVC的前端控制器&#xff0c;它可以拦截客户端的请求。拦截客户端请求之后&#xff0c;DispatcherServlet会根据具体规则…

chatgpt赋能Python-python3接口自动化

Python3接口自动化&#xff1a;提升测试效率的利器 Python是一种高级编程语言&#xff0c;广泛应用于Web开发、数据科学、机器学习等领域。近年来&#xff0c;Python在接口自动化测试领域也变得越来越受欢迎。 Python的易读性、可扩展性以及模块化的特性&#xff0c;使得它成为…