# 实现防止重复请求

​ 对于测试人员来说,重复点击按钮进行测试的这个动作怎一个熟练了得,如果没对按钮做防止重复请求的操作,测试人员将喜提一个bug并内心深处肯定是。。。(好菜)所以对按钮进行防止重复请求操作非常重要。

# 黄铜

​ 添加一个变量,判断是否点击了按钮,是的话再次点击直接return,否则触发请求

if (isLoading) return
isLoading = true
XXXApi.XXX().then(data => {
	......
    isLoading = false
}).catch(err => {
    isLoading = false
})

# 白银

​ 给按钮添加loading状态遮罩层,如el-button 添加 loading属性。多了loading动画,免了判断。

 <el-button class="two-words" type="primary" :loading="isLoading" @click="handleQuery">查询</el-button>

isLoading = true
XXXApi.XXX().then(data => {
	......
    isLoading = false
}).catch(err => {
    isLoading = false
})

# 铂金

​ 从根源处解决防止重复请求,当请求未响应时,遇到相同请求不予操作。

VUE项目利用Axios实现好用的API层请求接口封装 (opens new window)

# 钻石

​ 相对铂金的方式,解决了铂金的方式存在当响应较慢时,更改数据后再点击按钮后无做任何操作,导致提交的数据并非最新更改后的数据。因此还需要优化。

​ 使用axios取消请求的方式,实现对相同请求,取消前面的请求。

上代码

// request.js

import axios from "@/api/axios"

import originalAxios from "axios"

const GLOBAL_REQUEST_OBJ = {}
function newRequest(name, params) {
  return new Promise((resolve, reject) => {
    if (GLOBAL_REQUEST_OBJ[name]) {
      GLOBAL_REQUEST_OBJ[name].cancel(params.showLoading)
    }
    const CancelToken = originalAxios.CancelToken
    GLOBAL_REQUEST_OBJ[name] = CancelToken.source()
    Object.assign(params, {cancelToken: GLOBAL_REQUEST_OBJ[name].token})

    axios(params).then(result => {
      delete GLOBAL_REQUEST_OBJ[name]
      resolve(result)
    }).catch(err => {
      reject(err)
    })
  })
}

["get", "post", "delete", "put", "patch"].forEach(type => {
  newRequest[type] = (name, ...params) => {
    let isShowLoading = false
    if (params[1].params && (type === "get" || type === "delete") && !params[2]) {
      isShowLoading = params[1].showLoading || false
    } else {
      isShowLoading = params[2].showLoading || false
    }
    return new Promise((resolve, reject) => {
      if (GLOBAL_REQUEST_OBJ[name]) {
        GLOBAL_REQUEST_OBJ[name].cancel(isShowLoading)
      }

      const CancelToken = originalAxios.CancelToken
      GLOBAL_REQUEST_OBJ[name] = CancelToken.source()
      // 第二个参数对象 存在 params且是 get 请求 或者 delete 请求, 无第三个参数 config
      if (params[1].params && (type === "get" || type === "delete") && !params[2]) {
        params[1].cancelToken = GLOBAL_REQUEST_OBJ[name].token
      } else { // 配置config
        Object.assign(params[2], {cancelToken: GLOBAL_REQUEST_OBJ[name].token})
      }
      axios[type].apply(this, params).then(result => {
        delete GLOBAL_REQUEST_OBJ[name]
        resolve(result)
      }).catch(err => {
        reject(err)
      })
    })
  }
})

export const axiosRequest = newRequest

接口请求的地方使用

// common-api.js

import { axiosRequest } from "@/api/request"
import axios from "@/api/axios"

/**
 * @description: get请求示例
 * @param {*} data
 * @param {*} config
 * @return {*}
 * @author: syx
 */
export function getDemo(data = {}, config = {}) {
  const options = Object.assign({ params: data }, config)
  return axiosRequest.get("commmonApi" + "getDemo", `/url`, options)
}

/**
 * @description: post请求示例
 * @param {*}
 * @return {*}
 * @author: syx
 */
export function postDemo(data = {}, config = {}) {
  return axiosRequest.post("commmonApi" + "postDemo", "url", data, config)
}

/**
 * @description: delete请求示例1
 * @param {*}
 * @return {*}
 * @author: syx
 */
export function deleteDemo1(data = {}, config = {}) {
  return axiosRequest.delete("commmonApi" + "deleteDemo1", "url", data, config)
}

/**
 * @description:  delete请求示例2
 * @param {*} data
 * @param {*} config
 * @return {*}
 * @author: syx
 */
export function deleteDemo2(data = {}, config = {}) {
  const options = Object.assign({ params: data }, config)
  return axiosRequest.delete("commmonApi" + "deleteDemo2", `/url`, options)
}

/**
 * @description: put请求示例
 * @param {*}
 * @return {*}
 * @author: syx
 */
export function putDemo(data = {}, config = {}) {
  return axiosRequest.put("commmonApi" + "putDemo", "url", data, config)
}

/**
 * @description: patch请求示例
 * @param {*}
 * @return {*}
 * @author: syx
 */
export function patchDemo(data = {}, config = {}) {
  return axiosRequest.patch("commmonApi" + "patchDemo", "url", data, config)
}

# 总结

​ 对于黄铜和白银的方式,存在很多按钮时,需要定义多个变量,且项目中处处需要添加代码。

​ 铂金和钻石则采用从根源处解决问题,无需添加代码判断,钻石解决了铂金存在的问题,如有更好的方式,欢迎指导。

​ 此篇文章为axios请求优化其三,关于防止接口重复请求的优化,关注微信公众号【爆米花小布】不迷路。如有问题,可私信。

史上最牛逼的axios请求优化 (opens new window)