【Vue实战】Vuex 和 Axios 拦截器设置全局 Loading

目录

1. 效果图

 2. 思路分析

 2.1 实现思路

2.2 可能存在的问题

2.2.1 并发请求管理

2.2.2 请求快速响应和缓存带来的问题

3. 代码实现

4. 总结 


1. 效果图

如下图所示,当路由变化或发起请求时,出现 Loading 等待效果,此时页面不可见。当路由跳转结束或请求结束时,Loading效果隐藏,页面可见。

 2. 思路分析

接下来从思路和难点入手分析,最终代码实现,解决问题。

 2.1 实现思路

(1) 需要有一个 Loading.vue 组件,可以自己写,也可以用组件库中的组件。

(2) 创建 vuex,通过 vuex store 来管理一个 loading 状态。

(3) 需要在 store 中定义一个计数器,用来追踪正在请求的数量。

(4) 使用 Axios 拦截器,在每次请求开始的时候增加计数器,在请求完成的时候减少计数器。

(5) 根据计数器的值来控制 loading 状态,进而控制 Loading.vue 组件的显示隐藏。

2.2 可能存在的问题

2.2.1 并发请求管理

描述:当应用程序中有多个并发请求时,简单的开始和结束 loading 状态的方式可能会导致 loading 状态提前结束或不正确地显示。例如,如果在 endloading  被调用时还有其他请求未完成,loading 状态应该保持为 true。

参考方法:使用计数器追踪请求数量,如上述所说,Vuex store 中引入一个计数器来追踪正在进行中的请求数量。每次发起请求时增加计数器,请求完成后减少计数器。只有当所有请求都完成后(即计数器归零),才将 loading 置为 false。

2.2.2 请求快速响应和缓存带来的问题

描述:由于网络速度较快或浏览器缓存的原因,某些请求可能会非常快地完成,导致 loading 状态几乎不显示。此外,浏览器可能会从缓存中获取资源,从而绕过了正常的请求流程,使得 loading 状态没有机会被触发。

参考方法:在 endLoading 中引入一个最小延迟(我设置的 300 毫秒)。这样即使请求很快完成,loading 指示器也会显示足够长的时间让用户注意到。可以使用 setTimeOut 来实现这一功能。

3. 代码实现

(1) 如果没有就先下载 Axios 和 vuex。(我是在创建项目的时候就已经下载好了)

下载命令

npm install vuex --save

npm install axios --save

(2) 创建 store 文件,并在其中创建 index.js 文件来定义 Vuex store。然后在主文件 main.js 中引入并使用它。

创建文件夹

/store/index.js 

// 引入 Vue 和 Vuex 库
import Vue from 'vue';
import Vuex from 'vuex';

// 使用 Vuex 插件
Vue.use(Vuex);

// 创建 Vuex Store 实例
const store = new Vuex.Store({
  // 定义 state,包含应用程序的状态数据
  state: {
    isLoading: false, // 当前是否处于加载状态
    pendingRequests: 0, // 用来追踪并发请求的数量
  },
  
  // 定义 mutations,用于同步地修改 state
  mutations: {
    // 增加正在处理的请求数量
    incrementPendingRequests(state) {
      state.pendingRequests++;
      // 如果这是第一个请求,则设置加载状态为 true
      if (state.pendingRequests === 1) {
        state.isLoading = true;
      }
    },

    // 减少正在处理的请求数量
    decrementPendingRequests(state) {
      if (state.pendingRequests > 0) {
        state.pendingRequests--;
        // 如果所有请求都已完成,则设置加载状态为 false
        if (state.pendingRequests === 0) {
          state.isLoading = false;
        }
      }
    }
  },

  // 定义 actions,用于异步操作和触发 mutations
  actions: {
    // 开始加载状态,通过调用 mutation 增加请求数量
    startLoading({ commit }) {
      commit('incrementPendingRequests');
    },

    // 结束加载状态,通过调用 mutation 减少请求数量
    endLoading({ commit }) {
      commit('decrementPendingRequests');
    },

    // 延迟结束加载状态,在指定时间后调用 endLoading action
    delayedEndLoading({ dispatch }, delay = 0) {
      setTimeout(() => {
        dispatch('endLoading');
      }, delay); // 默认延迟时间为 0 毫秒
    }
  },

  // 定义 getters,用于从 state 中派生出一些状态
  getters: {
    // 获取当前的加载状态
    isLoading: state => state.isLoading
  }
});

// 导出 store 实例,以便在应用程序中使用
export default store;

详细注释已在代码中呈现 ↑ ↑ ↑ 

main.js 文件引入 

(3) 创建 axios 文件夹以及相关文件(/api/apiClient.js),添加相应拦截器和请求拦截器,在这里面下文章。

创建文件夹

apiClient.js 文件

// 引入 Axios 库和 Vuex store 实例
import axios from 'axios';
import store from '../store'; 

// 创建自定义 Axios 实例,用于发起 HTTP 请求
const apiClient = axios.create({
    baseURL: process.env.VUE_APP_API_URL, // 使用环境变量设置基础 URL,方便跨环境配置
    timeout: 1000, // 设置请求超时时间为 1 秒
    headers: { 'Content-Type': 'application/json' } // 设置默认请求头为 JSON 格式
});

// 添加请求拦截器,拦截所有发出的请求
apiClient.interceptors.request.use(
    config => {
        // 每次请求开始时,调用 Vuex store 的 startLoading action 开始加载状态
        store.dispatch('startLoading');
        // 返回配置对象,允许请求继续进行
        return config;
    },
    error => {
        // 如果请求在发送前发生错误(例如网络错误),调用 endLoading 结束加载状态
        store.dispatch('endLoading');
        // 返回一个被拒绝的 Promise,以便处理错误
        return Promise.reject(error);
    }
);

// 添加响应拦截器,拦截所有接收到的响应
apiClient.interceptors.response.use(
    response => {
        // 成功接收到响应后,调用 delayedEndLoading action,在延迟 0.5 秒后结束加载状态
        store.dispatch('delayedEndLoading', 500); // 成功响应后延迟 0.5 秒结束 loading
        // 返回响应对象,允许后续处理
        return response;
    },
    error => {
        // 如果响应失败(例如服务器返回错误码),立即调用 endLoading 结束加载状态
        store.dispatch('endLoading');
        // 返回一个被拒绝的 Promise,以便处理错误
        return Promise.reject(error);
    }
);

// 导出自定义 Axios 实例,以便在整个应用程序中使用
export default apiClient;

详细解说见代码注释 ↑ ↑ ↑ 

(4) 自定义一个 Loading 组件,代码如下。 

Loading.vue 文件

<template>
  <!-- 使用 v-if 指令根据 isLoading 状态显示或隐藏加载指示器 -->
  <div v-if="isLoading" class='base'>
    <!-- 加载动画图片,当 isLoading 为 true 时显示 -->
    <img src="../assets/images/preloader.gif" alt="Loading...">
  </div>
</template>

<script>
export default {
  name: 'Loading',
  data() {
    return {
    }
  },

  mounted() {
    
  },
  computed: {
    isLoading() {
      // 通过 this.$store.getters 访问 Vuex store 的 getters,
      // 并返回 isLoading 状态,以控制加载指示器的显示与否
      return this.$store.getters.isLoading;
    }
  }
}
</script>

<style scoped>
body {
  background-color: white; 
  font-size: 12px;        
}

/* 加载指示器容器样式 */
.base {
  position: absolute;     
  top: 50%;               
  left: 50%;              
  transform: translate(-50%, -50%);
  z-index: 9999;          
}
</style>

(5) 实现路由跳转时 loading 效果的呈现。

只需在 /router/index.js 中添加如下代码

// 全局前置守卫
router.beforeEach((to, from, next) => {
  // 开始加载状态
  store.dispatch('startLoading');

  // 继续导航
  next();
});

// 后置钩子
router.afterEach(() => {
  // 这里可以设置一个短暂的延迟来模拟loading效果,或者直接结束loading
  setTimeout(() => {
    store.dispatch('endLoading');
  }, 300); // 可选的延迟时间
});

(6) 在 App.vue 入口文件,放置 Loading.vue组件,当 store 中的 loading 为 true 时,就用Loading.vue组件遮住页面,达到加载中的效果。

App.vue文件

<template>
   <div id="app" :class="{ 'is-loading': $store.getters.isLoading }">
    <router-view />
    <Loading /> 
  </div>
</template>

<script>
import Loading from './components/Loading.vue'
export default {
  name: 'App',
  components: {
    Loading,
  }
}
</script>

<style>
*{margin: 0;padding: 0;}
li{list-style:none}
a{text-decoration:none}
#app{
  height: 100vh;
}
#app.is-loading::before {
  content: '';
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: white;
  z-index: 9998; /* 确保它在所有元素之上但低于loading组件 */
  pointer-events: none; /* 不阻止点击事件传递到下面的loading组件 */
}

</style>

注意: 

Loading.vue组件需要将页面遮住,需要提一下的就是层级的问题注意一下。

 (7) 然后在页面请求接口或则路由跳转时,就会出现 loading 效果

4. 总结 

最主要的就是vuex的store中的部分逻辑,以及相应拦截器和请求拦截器调用store中的,总结一下实现步骤吧。(1) 引入必要的库  (2) 创建 Vuex Store  (3) 配置 Axios 实例  (4) 创建加载组件  (5) 将组件集成到应用

如果以上内容对你有用的话不妨点赞、关注+收藏,防止下次迷路😀。

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

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

相关文章

跨境电商领域云手机之选:亚矩阵云手机的卓越优势

在跨境电商蓬勃发展的当下&#xff0c;云手机已成为众多企业拓展海外市场的得力助手。亚矩阵云手机凭借其独特优势&#xff0c;在竞争激烈的云手机市场中崭露头角。不过&#xff0c;鉴于市场上云手机服务供应商繁多&#xff0c;企业在抉择时需对诸多要素予以审慎考量。 跨境电商…

第三十八章 Spring之假如让你来写MVC——适配器篇

Spring源码阅读目录 第一部分——IOC篇 第一章 Spring之最熟悉的陌生人——IOC 第二章 Spring之假如让你来写IOC容器——加载资源篇 第三章 Spring之假如让你来写IOC容器——解析配置文件篇 第四章 Spring之假如让你来写IOC容器——XML配置文件篇 第五章 Spring之假如让你来写…

了解 ASP.NET Core 中的中间件

在 .NET Core 中&#xff0c;中间件&#xff08;Middleware&#xff09; 是处理 HTTP 请求和响应的核心组件。它们被组织成一个请求处理管道&#xff0c;每个中间件都可以在请求到达最终处理程序之前或之后执行操作。中间件可以用于实现各种功能&#xff0c;如身份验证、路由、…

kalilinux - 目录扫描之dirsearch

情景导入 先简单介绍一下dirsearch有啥用。 假如你现在访问一个网站&#xff0c;例如https://www.example.com/ 它是一个电商平台或者其他功能性质的平台。 站在开发者的角度上思考&#xff0c;我们只指导https://www.example.com/ 但不知道它下面有什么文件&#xff0c;文…

DHCP、MSTP+VRRP总结实验

R1即使服务器&#xff08;给予dhcp的地址的&#xff09; [LSW1]int Eth-Trunk 12 [LSW1-Eth-Trunk12]mode manual load-balance //配置链路聚合模式为手工负载分担模式 [LSW1-Eth-Trunk12]load-balance src-dst-mac //配置基于源目IP的负载分担模式[LSW1-Eth-Trunk12]trunk p…

【爬虫】单个网站链接爬取文献数据:标题、摘要、作者等信息

源码链接&#xff1a; https://github.com/Niceeggplant/Single—Site-Crawler.git 一、项目概述 从指定网页中提取文章关键信息的工具。通过输入文章的 URL&#xff0c;程序将自动抓取网页内容 二、技术选型与原理 requests 库&#xff1a;这是 Python 中用于发送 HTTP 请求…

RabbitMQ故障全解析:消费、消息及日常报错处理与集群修复

文章目录 前言&#xff1a;1 消费慢2 消息丢失3 消息重复消费4 日常报错及解决4.1 报错“error in config file “/etc/rabbitmq/rabbitmq.config” (none): no ending found”4.2 生产者发送消息报错4.3 浏览器打开IP地址&#xff0c;无法访问 RabbitMQ&#xff08;白屏没有结…

C#格式化输出

C#提供了多个格式化输出的方法&#xff0c;使得我们在灵活且可读的方法构建字符串&#xff1b;主要的格式化方法包括&#xff1a;String.Format方法、字符串插值&#xff0c;以及使用符合格式字符串与Console.WriteLint或Console.Write函数。 String.Format方法 string.Format…

记一次学习skynet中的C/Lua接口编程解析protobuf过程

1.引言 最近在学习skynet过程中发现在网络收发数据的过程中数据都是裸奔&#xff0c;就想加入一种数据序列化方式&#xff0c;json、xml简单好用&#xff0c;但我就是不想用&#xff0c;于是就想到了protobuf&#xff0c;对于protobuf C/C的使用个人感觉有点重&#xff0c;正好…

vue2修改表单只提交被修改的数据的字段传给后端接口

效果&#xff1a; 步骤一、 vue2修改表单提交的时候&#xff0c;只将修改的数据的字段传给后端接口&#xff0c;没有修改得数据不传参给接口。 在 data 对象中添加一个新的属性&#xff0c;用于存储初始表单数据的副本&#xff0c;与当前表单数据进行比较&#xff0c;找出哪些…

大数据运维管理体系的搭建

[〇]关于本文 本文将介绍一种大型集群的运维管理体系 【大型集群的管理大于解决问题】意在大规模数据集群的运维过程中&#xff0c;系统化、规范化的管理措施比单纯的故障处理更为关键。通过有效的管理&#xff0c;可以预防问题的发生、提升系统的稳定性和性能&#xff0c;从而…

如何使用开源图床程序EasyImage搭建一个私有图库并实现远程传图

前言&#xff1a;在输出内容时&#xff0c;一张高质量的图片往往能够瞬间吸引读者的眼球&#xff0c;提升内容的整体价值。然而&#xff0c;对于许多博主、站长和自媒体人来说&#xff0c;找到一个稳定且免费的图床服务却成了头疼的问题。很多图床要么不稳定&#xff0c;导致图…

Java Web开发进阶——错误处理与日志管理

错误处理和日志管理是任何生产环境中不可或缺的一部分。在 Spring Boot 中&#xff0c;合理的错误处理机制不仅能够提升用户体验&#xff0c;还能帮助开发者快速定位问题&#xff1b;而有效的日志管理能够帮助团队监控应用运行状态&#xff0c;及时发现和解决问题。 1. 常见错误…

二分查找算法——山脉数组的峰顶索引

一.题目描述 852. 山脉数组的峰顶索引 - 力扣&#xff08;LeetCode&#xff09; 二.题目解析 题目给了我们一个山脉数组&#xff0c;山脉数组的值分布就如下面的样子&#xff1a; 然后我们只需要返回数组的峰值元素的下标即可。 三.算法原理 1.暴力解法 因为题目明确说明…

2. Doris数据导入与导出

一. Doris数据导入 导入方式使用场景支持的文件格式导入模式Stream Load导入本地文件或者应用程序写入csv、json、parquet、orc同步Broker Load从对象存储、HDFS等导入csv、json、parquet、orc异步Routine Load从kakfa实时导入csv、json异步 1. Stream Load 基本原理 在使用…

30_Redis哨兵模式

在Redis主从复制模式中,因为系统不具备自动恢复的功能,所以当主服务器(master)宕机后,需要手动把一台从服务器(slave)切换为主服务器。在这个过程中,不仅需要人为干预,而且还会造成一段时间内服务器处于不可用状态,同时数据安全性也得不到保障,因此主从模式的可用性…

把PX4及子仓库添加到自己的gitee

导入主仓库 此处以导入PX4为例 先用gitee导入仓库然后clone gitee仓库先checkout到v1.11&#xff0c;git submodule update --init --recursive&#xff0c;确保可以make之后再新建branchgit checkout -b my1.11.0按照提示连接到origin改代码然后三件套就行了git add ./*git …

解决:ubuntu22.04中IsaacGymEnv保存视频报错的问题

1. IsaacGymEnvs项目介绍 IsaacGymEnvs&#xff1a;基于NVIDIA Isaac Gym的高效机器人训练环境 IsaacGymEnvs 是一个基于 NVIDIA Isaac Gym 的开源 Python 环境库&#xff0c;专为机器人训练提供高效的仿真环境。Isaac Gym 是由 NVIDIA 开发的一个高性能物理仿真引擎&#xf…

ELK日志分析实战宝典之ElasticSearch从入门到服务器部署与应用

目录 ELK工作原理展示图 一、ElasticSearch介绍&#xff08;数据搜索和分析&#xff09; 1.1、特点 1.2、数据组织方式 1.3、特点和优势 1.3.1、分布式架构 1.3.2、强大的搜索功能 1.3.3、数据处理与分析 1.3.4、多数据类型支持 1.3.5、易用性与生态系统 1.3.6、高性…

android 自定义SwitchCompat,Radiobutton,SeekBar样式

纯代码的笔记记录。 自定义SwitchCompat按钮的样式 先自定义中间的圆球switch_thumb_bg.xml <?xml version"1.0" encoding"utf-8"?> <shape xmlns:android"http://schemas.android.com/apk/res/android"android:shape"oval&q…