利用 axios 阻止重复请求

184 阅读1分钟

利用 axios 阻止重复请求

前端重复请求是一个很常见的问题,现有的解决方案有很多,如:

  • 申明一个状态变量
  • debounce 和 throttle
  • axios 的 CancelToken

申明变量方式

 // 伪代码
 let isShow = false      // 初始状态
 ​
 async handleFunc () {
   isShow = true     // isShow 为 true 时,按钮置灰等
   await this.$apis.xxx()
   isShow = false
 }
 ​
 // 这种方式的劣势是和业务代码耦合,每个请求都需要设置状态变量控制
 复制代码

debounce 和 throttle

防抖和节流函数也是针对于频繁的事件触发,如 resize、input、scroll 等,具体可以看附录大佬的文章,今天主要看如何利用 axios 来全局阻止重复请求

axios 的 CancelToken

在我们的项目中,一般都会对 axios 进行二次封装去使用,对请求、响应拦截器进行全局操作,如 HTTP 状态码、业务标识处理、全局 loading 以及我们今天说的阻止重复请求

axios 中是利用 CancelToken 来中止请求的,其实这就是原生 xhr.abort() ,具体逻辑如下:

 import axios, { AxiosResponse, AxiosRequestConfig, AxiosError } from 'axios'
 import router from '@/router'
 import Vue from 'vue'
 ​
 const flagMsg: string = 'FASTCLICK' // 连续点击错误标识
 let pending: any[] = []
 const cancelToken = axios.CancelToken // 初始化取消请求的构造函数
 ​
 const removePending = (config: any, fn?: any) => {
   const arr = config.url.split('/api') // 处理 baseURL
   const flagUrl = arr[arr.length - 1]
   if (pending.includes(flagUrl)) {
     fn ? fn(flagMsg) : pending.splice(pending.indexOf(flagUrl), 1)
   } else {
     if (fn) {
       pending.push(flagUrl)
     }
   }
 }
 ​
 // 创建 axios 实例
 const instance = axios.create({
   baseURL: '/api',  // api 的 base_url
   timeout: 60000,   // 请求超时时间
 })
 ​
 /**
  * request 拦截器
  * @param config request拦截器
  */
 const requestInterceptor = (config: AxiosRequestConfig) => {
   config.cancelToken = new cancelToken(c => {
     removePending(config, c)
   })
   return config
 }
 ​
 /**
  * request 拦截器
  * @param response 返回拦截器
  */
 const responseInterceptor = (response: AxiosResponse<any>) => {
   removePending(response.config)
   const code = response.data.code
   switch (code) {
     case 'LOGIN_FAILED':
     // 省略业务状态码
     case 'TOKEN_EXPIRED': {
       logOut() // 退出登录
       setTimeout(() => {
         router.push({ name: 'SignIn' })
       }, 800)
     }
     default:
       break
   }
   return response
 }
 ​
 /**
  * 请求错误处理
  * @param error error 实例
  */
 const errorRequest = (error: AxiosError) => {
   Vue.$handleError(error)
 }
 ​
 /**
  * 响应错误处理
  * @param error error 实例
  */
 const errorResponse = (error: AxiosError) => {
   pending = []
   if (error.message === flagMsg) {
     throw new Error('Jangan sering klik')
   }
   Vue.$handleError(error)
 }
 ​
 instance.interceptors.request.use(requestInterceptor, errorRequest)
 instance.interceptors.response.use(responseInterceptor, errorResponse)
 ​
 复制代码

以上我们可以阻止点击造成的频繁请求,后续还可以加上全局 loading 或者 token 等,这个部分就不在这里写了,可以自己尝试一下