Vue Axios请求取消和重发封装的实现代码

 更新时间:2024年09月12日 09:39:02   作者:墨辰mc  
这篇文章主要介绍了Vue Axios请求取消和重发的封装的实现,文章通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下

场景

如图,有时候请求后端同一接口时,会因接口响应速度过慢,导致前端原本页面应显示a,结果显示b。像上图,前端页面看起来就是先显示 请求b 的数据,然后是 请求c 的数据,最后是 请求a 的数据,但是实际上我们只需要显示最后的 请求c 的数据。而且这样还会导致页面渲染出现闪烁。

需求

  • 实现取消重复请求(有多种取消方式)
    • 发出第一个请求,取消后面的相同请求
    • 取消前面的请求,只发出最后一个请求
  • 实现请求失败后,自动重发指定次数

实现1(发出第一个请求,取消后面的相同请求)

import Axios from "axios";

// 创建一个实例
const service = Axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 1000,
});

// 添加请求拦截器
service.interceptors.request.use(
  (config) => {
    // 判断是否有 token
    if (getToken()) {
      config.headers["Permission-Token"] = getToken();
    }
    // 添加待处理请求
    addPendingRequest(config);
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 添加响应拦截器
service.interceptors.response.use(
  (response) => {
    // 移除挂起的请求
    removePendingRequest(response);
    const res = response.data;
    return res;
  },
  (error) => {
    // 移除挂起的请求
    removePendingRequest(error);

    // 是否是取消请求
    if (Axios.isCancel(error)) {
      console.log("err:", error);
      return Promise.reject(error);
    } else {
      console.log("请求有问题:", error.message);
      // 当请求超时、断网等问题时重发
      return againRequest(error);
    }
  }
);

export default service;

const pendingRequest = new Map();
/**
 * 添加待处理请求
 * @description 该函数用于管理重复请求,避免同一接口的多个请求同时进行
 * @param {Object} config - axios请求配置对象
 */
function addPendingRequest(config) {
  // 创建axios的取消令牌源,用于请求的取消控制
  const CancelToken = axios.CancelToken;
  const source = CancelToken.source();

  // 生成请求的唯一键,用于标识不同的请求
  const requestKey = generateReqKey(config);

  // 方法一
  // 如果相同请求已在进行中,则取消当前请求
  if (pendingRequest.has(requestKey)) {
    config.cancelToken = source.token;
    source.cancel(`${config.url} 请求已取消`);
  } else {
    // 如果没有相同的请求在进行中,则设置取消令牌并存储请求键和取消函数的映射
    config.cancelToken = source.token;
    pendingRequest.set(requestKey, source.token);
  }
  
  // 方法二
  // 如果相同请求已在进行中,则取消当前请求
  if (pendingRequest.has(requestKey)) {
     config.cancelToken = new Axios.CancelToken((cancel) => {
       // cancel 函数的参数会作为 promise 的 error 被捕获
       cancel(`${config.url} 请求已取消`);
     });
  } else {
     config.cancelToken =
       new Axios.CancelToken((cancel) => {
         pendingRequest.set(requestKey, cancel);
       });
  }
  
   // 以上两种方式都能取消请求,但是从 Axios `v0.22.0` 开始已被弃用,从 `v0.22.0` 开始,Axios 支持以 fetch API 方式—— `AbortController`取消请求
}

/**
 * 移除挂起的请求
 * @description 当收到响应时,检查并移除与该响应相关的挂起请求
 * @param {Object} response - 响应对象,包含请求配置信息
 */
function removePendingRequest(response) {
  // 如果 response.config 返回的是 undefined,表示是取消请求,则不处理
  if (response.config) {
    // 生成请求的唯一键,用于标识不同的请求
    const requestKey = generateReqKey(response.config);

    // 判断是否有这个 key,有就删除
    if (pendingRequest.has(requestKey)) {
      pendingRequest.delete(requestKey);
    }
  }
}

/**
 * 处理请求重试的函数
 * @description 它尝试重新发送因错误而失败的请求
 * @param {Object} err - 包含错误信息和配置的对象
 * @returns {Promise} 重新发送请求的Promise对象,解决或拒绝依据重试逻辑
 */
function againRequest(err) {
  const config = err.config;

  // 如果配置对象不存在,则直接拒绝Promise
  if (!config) return Promise.reject(err);

  // 设置用于记录重试计数的变量 默认为0
  config._retryCount = config._retryCount || 0;

  // 如果重试次数已经达到3次或更多,则拒绝Promise
  if (config._retryCount >= 3) {
    return Promise.reject(err);
  }
  // 重试次数
  config._retryCount += 1;

  // 延时处理,用于控制重试的时间间隔
  var backoff = new Promise((resolve) => {
    setTimeout(() => {
      resolve();
    }, 1000);
  });

  // 在延时结束后,重新发起请求
  return backoff.then(() => {
    // 重新发起axios请求,使用更新后的配置
    return service(config);
  });
}

/**
 * 生成请求的唯一键
 * @description 这个函数的目的是根据请求配置生成一个唯一的键,用于缓存请求数据
 * @param {Object} config - 请求配置对象,包含了url、method、params和data属性
 * @returns {string} - 返回一个根据请求配置生成的唯一键
 */
function generateReqKey(config) {
  // 请求得到的config.data是对象格式,响应回来的response.config.data是字符串格式,需要转换成对象
  if (typeof config.data === "string") {
    config.data = JSON.parse(config.data);
  }
  const { url, method, params, data } = config;

  return [url, method, JSON.stringify(params), JSON.stringify(data)].join("&");
}

实现2(取消前面的请求,只发出最后一个请求)

import Axios from "axios";

// 创建一个实例
const service = Axios.create({
  baseURL: process.env.VUE_APP_BASE_API,
  timeout: 1000,
});

// 添加请求拦截器
service.interceptors.request.use(
  (config) => {
    // 判断是否有 token
    if (getToken()) {
      config.headers["Permission-Token"] = getToken();
    }
    // 添加待处理请求
    addPendingRequest(config);
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// 添加响应拦截器
service.interceptors.response.use(
  (response) => {
    const res = response.data;
    return res;
  },
  (error) => {
    // 是否是取消请求
    if (!axios.isCancel(error)) {
      // 当请求超时、断网等问题时重发
      againRequest(error);
    }
  }
);

export default service;

let pendingRequsetList = [];

/**
 * 取消所有待处理的请求
 */
export const cancelPendingRequest = () => {
  // 遍历待处理的请求列表,调用每个请求的取消方法
  pendingRequsetList.forEach((item) => {
    item.cancel();
  });
  // 清空待处理的请求列表
  pendingRequsetList = [];
};

/**
 * 添加待处理的请求
 * @description 为配置对象添加取消令牌,以便在请求待处理时能够取消
 * @param {Object} config - 请求的配置对象
 */
function addPendingRequest(config) {
  // 创建一个新的取消令牌,并将取消函数存储到待处理请求列表中
  config.cancelToken = new Axios.CancelToken((cancel) => {
    pendingRequsetList.push({
      cancel,
    });
  });
}

/**
 * 处理请求失败后的重试机制
 * @description 当请求失败时,尝试再次请求,最多重试3次
 * @param {Object} err - 错误对象,包含请求的配置信息
 * @returns {Promise} 重试请求的Promise对象
 */
function againRequest(err) {
  // 获取请求的配置信息
  const config = err.config;

  // 如果没有配置信息,则直接拒绝Promise
  if (!config) Promise.reject(err);

  // 初始化重试次数,如果不存在则为0
  config._retryCount = config._retryCount || 0;

  // 如果重试次数已经达到3次,则拒绝Promise
  if (config._retryCount >= 3) {
    return Promise.reject(err);
  }

  // 每次重试前,重试次数加1
  config._retryCount += 1;

  // 创建一个Promise,用于实现指数退避机制
  const backoff = new Promise((resolve) => {
    // 设置一个定时器,1秒后 resolve Promise
    setTimeout(() => {
      resolve();
    }, 1000);
  });

  // 返回一个Promise,表示再次请求的操作
  return backoff.then(() => {
    return service(config);
  });
}

在组件中使用,发送一个新的请求之前,取消前面的所有正在请求的请求,如下:

<script>
import { cancelPendingRequest } from '@/utils/request' // 导入取消请求的方法

export default {
  methods: {
    getList() {
      cancelPendingRequest() // 取消所有正在发送请求的请求
      axios.get() // 发送新的请求
    }
  }
}
</script>

如果不想每次使用取消请求的时候都需要导入那么可以挂载到Vue的原型上面,可以在main.js文件中

import Vue from 'vue'
import { cancelPendingRequest } from '@/utils/request'

Vue.prototype.$cancelPendingRequest = cancelPendingRequest // 挂载到 Vue 原型上

另外如果想每次路由页面跳转时,取消上一个页面的所有请求,我们可以在全局路由守卫那里进行处理,具体如下:

import router from "./router";
import Vue from 'vue'

router.beforeEach((to, from, next) => {
   Vue.$cancelPendingRequest() // 跳转页面之前取消之前的所有请求
})

以上就是Vue Axios请求取消和重发的封装的实现的详细内容,更多关于Vue Axios请求取消和重发的资料请关注脚本之家其它相关文章!

相关文章

  • Vue3项目打包后部署到服务器 请求不到后台接口解决方法

    Vue3项目打包后部署到服务器 请求不到后台接口解决方法

    在本篇文章里小编给大家整理了关于Vue3项目打包后部署到服务器 请求不到后台接口解决方法,有需要的朋友们可以参考下。
    2020-02-02
  • vue中组件之间相互传值的6种方法小结

    vue中组件之间相互传值的6种方法小结

    Vue.js 中组件间通信的方法有很多种,这篇文章主要为大家详细介绍了6种常见的直接或间接的组件传值方式,有需要的小伙伴可以参考一下
    2024-01-01
  • 使用Vue3和ApexCharts实现交互式3D折线图

    使用Vue3和ApexCharts实现交互式3D折线图

    ApexCharts 是一个功能强大的 JavaScript 库,用于创建交互式、可定制的图表,在 Vue.js 中,它可以通过 vue3-apexcharts 插件轻松集成,本文给大家介绍了使用Vue3和ApexCharts实现交互式3D折线图,需要的朋友可以参考下
    2024-06-06
  • 关于vue项目一直出现 sockjs-node/info?t=XX的解决办法

    关于vue项目一直出现 sockjs-node/info?t=XX的解决办法

    sockjs-node 是一个JavaScript库,提供跨浏览器JavaScript的API,创建了一个低延迟、全双工的浏览器和web服务器之间通信通道,这篇文章主要介绍了vue项目一直出现 sockjs-node/info?t=XX的解决办法,需要的朋友可以参考下
    2023-12-12
  • 分分钟玩转Vue.js组件

    分分钟玩转Vue.js组件

    这篇文章教大家如何分分钟玩转Vue.js组件,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2016-10-10
  • Vue Elenent实现表格相同数据列合并

    Vue Elenent实现表格相同数据列合并

    这篇文章主要为大家详细介绍了Vue Elenent实现表格相同数据列合并,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-11-11
  • 利用Electron简单撸一个Markdown编辑器的方法

    利用Electron简单撸一个Markdown编辑器的方法

    这篇文章主要介绍了利用Electron简单撸一个Markdown编辑器的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-06-06
  • 在vue中:style 的使用方式汇总

    在vue中:style 的使用方式汇总

    在Vue开发中使用:style绑定样式是常见需求,应注意:class与:style的配合使用,错误的使用可能导致样式不生效,正确的方法是使用数组绑定多个样式,这些技巧有助于提高开发效率和保持代码整洁,感兴趣的朋友跟随小编一起看看吧
    2024-09-09
  • Vue父组件和子组件之间数据传递和方法调用

    Vue父组件和子组件之间数据传递和方法调用

    vue组件在通信中,无论是子组件向父组件传值还是父组件向子组件传值,他们都有一个共同点就是有中间介质,子向父的介质是自定义事件,父向子的介质是props中的属性。
    2022-12-12
  • Ant Design Vue中的table与pagination的联合使用方式

    Ant Design Vue中的table与pagination的联合使用方式

    这篇文章主要介绍了Ant Design Vue中的table与pagination的联合使用方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-10-10

最新评论