axios 全局阻止重复请求

在项目中,我们可能需要对请求进行‘防抖’处理。主要为了阻止用户在某些情况下,短时间内重复点击某个按钮,导致前端向后端重复发送多次请求。常见的情况:

    PC 端 - 用户双击搜索按钮/提交表单,可能会触发两次搜索请求/重复提交数据

    移动端 - 因移动端没有点击延迟,因此极容易造成误操作或者多操作,造成重复请求

可能有 loading 遮罩层依然发生,因此我们要考虑前端阻止重复请求的方法。

首先我们需要了解一下 axios 库中的 cancelToken API,其主要是用于取消接口请求的核心 API。在官网文档中主要有两个方法使用,代码如下:

方法一:

const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {  
  cancelToken: source.token // 必须对请求进行cancelToken设置
}).catch(function (thrown) {
  if (axios.isCancel(thrown)) { // 如果请求被取消则进入该方法判断
    console.log('Request canceled', thrown.message);
  } else {
    // handle error
  }
});

// 取消上面的请求
// source.cancel('messge') message为可选项,必须为String
source.cancel('Operation canceled by the user.');

方法二:

const CancelToken = axios.CancelToken;
let cancel;

axios.get('/user/12345', {
  // 在options中直接创建一个cancelToken对象
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  })
});

// 取消上面的请求
cancel();

有了官方提供的 API,那我们如何封装用于项目中呢?axios 作为请求的配置,有请求拦截器和响应拦截器作为全局请求的配置管理。首先我们对所有正在进行中的请求进行缓存,在请求发起前判断请求是否正在进行,如果有,则取消该请求。在完成请求之后,我们需要将缓存列表中的该次请求删除,以便可以重新发送该请求。

// request拦截器
$http.interceptors.request.use(config => {
  // 如果想请求可以重复发起,给在请求参数中加allowedRepeat:true  (后续会删除,不会发送给服务端)
  if (!config.data || !config.data.allowedRepeat) { // 如果不允许重复请求,开启拦截
    // todo: 1. 设置拦截 防止重复请求
    // 拦截重复请求(即当前正在进行的相同请求)
    const requestData = getRequestIdentify(config)
    removePending(requestData, true)
    // 使用 cancel token 取消请求 参考:http://www.axios-js.com/zh-cn/docs/#%E6%8B%A6%E6%88%AA%E5%99%A8
    config.cancelToken = new CancelToken((c) => {
      pending[requestData] = c
    })
  } else { // 允许重复请求,不进行拦截
    delete config.data.allowedRepeat // 把自定义的请求参数给删掉,不发送给服务端
  }
  return config
}, error => {
  // Do something with request error
  console.log(error) // for debug
  Promise.reject(error)
})

getRequestIdentify 方法将请求 url 和请求参数作为是否是同一请求的判断;

removePending 方法是利用对象的 key 是否存在判断是否为需要取消重复请求;

这里的 key 就是有请求的 url 和 请求参数组合而成。代码实现如下:

// 拦截重复请求
let pending = {}
const CancelToken = axios.CancelToken
// 请求标识;完成请求后也需要执行删除记录,所以添加此参数避免执行无用操作
const removePending = (key, isRequest = false) => {
  if (pending[key] && isRequest) {
    pending[key]('取消重复请求')
  }
  delete pending[key]
}
/**
 * 由于我们请求用了代理 直接代理到测试服务器 因此请求响应拦截器的config.url是一致的,不需要标识值区分
 * 如果请求拦截器和响应拦截器的config.url不一致,就需要一个标识值用来区分判断
 */
const getRequestIdentify = (config) => {
  const url = config.url
  // 返回url及请求参数 post方法请求参数为config.data  get方法请求参数为config.params
  if (config.method === 'post') {
    return encodeURIComponent(config.url + JSON.stringify(config.data))
  }
  return encodeURIComponent(url + JSON.stringify(config.params))
}

 

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值