axios请求封装拦截重复请求

常见的业务需求,就是点击按钮,还没有反馈,你又点了一下。导致请求了两次。当然前端可以做按钮禁用,等待反馈。tab栏频繁切换。

如果不想看细节,直接使用,后面有完整的axios封装。

一、取消请求
原生
let xhr = new XMLHttpRequest();
xhr.open("GET", "http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10", true);
xhr.send();
setTimeout(() => xhr.abort(), 300);

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

axios.get('http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10', {
  cancelToken: source.token
})

source.cancel('取消请求.'); 

此外,你也可以通过调用 CancelToken 的构造函数来创建 CancelToken,具体如下所示:

const CancelToken = axios.CancelToken;
let cancel;

axios.get('http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10', {
  cancelToken: new CancelToken(function executor(c) {
    cancel = c;
  })
});

cancel(); // 取消请求
二、判断重复请求

当请求方式、请求 URL 地址和请求参数都一样时,我们就可以认为请求是一样的。因此在每次发起请求时,我们就可以根据当前请求的请求方式、请求 URL 地址和请求参数来生成一个唯一的 key,同时为每个请求创建一个专属的 CancelToken,然后把 key 和 cancel 函数以键值对的形式保存到 Map 对象中,使用 Map 的好处是可以快速的判断是否有重复的请求:

import Qs from "qs";
const pendingRequest = new Map();
// 用于根据当前请求的信息,生成请求 Key
function generateReqKey(config) {
  const { method, url, params, data } = config;
  return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}

function addPendingRequest(config) {  // 每次请求追加该方法
  const requestKey = generateReqKey(config);
  config.cancelToken =
    config.cancelToken ||
    new axios.CancelToken((cancel) => {
      if (!pendingRequest.has(requestKey)) {
        pendingRequest.set(requestKey, cancel); // pendingRequest 对应好键 值
      }
    });
}

三、重复请求时取消
// 检查是否存在重复请求,若存在则取消已发的请求
function removePendingRequest(config) {
  const requestKey = generateReqKey(config);
  if (pendingRequest.has(requestKey)) {
    const cancelToken = pendingRequest.get(requestKey);
    cancelToken(requestKey);
    pendingRequest.delete(requestKey);
  }
}

四、整合axios封装

在这里插入图片描述
axios.js文件
Ctrl+A Ctrl+C、Ctrl+V 即可(根据实际项目微调)

import axios from "axios";
import { get } from "lodash";
import Qs from "qs";

// 用于根据当前请求的信息,生成请求 Key
function generateReqKey(config) {
  const { method, url, params, data } = config;
  return [method, url, Qs.stringify(params), Qs.stringify(data)].join("&");
}

// 用于把当前请求信息添加到pendingRequest对象中
const pendingRequest = new Map();

function addPendingRequest(config) {
  const requestKey = generateReqKey(config);
  config.cancelToken =
    config.cancelToken ||
    new axios.CancelToken((cancel) => {
      if (!pendingRequest.has(requestKey)) {
        pendingRequest.set(requestKey, cancel);
      }
    });
}

// 检查是否存在重复请求,若存在则取消已发的请求
function removePendingRequest(config) {
  const requestKey = generateReqKey(config);
  if (pendingRequest.has(requestKey)) {
    const cancelToken = pendingRequest.get(requestKey);
    cancelToken(requestKey);
    pendingRequest.delete(requestKey);
  }
}

/**
 * @description 创建请求实例
 */
function createService() {
  // 创建一个 axios 实例
  const service = axios.create();
  // 请求拦截
  service.interceptors.request.use(
    (config) => {
      removePendingRequest(config); // 检查是否存在重复请求,若存在则取消已发的请求
      addPendingRequest(config); // 把当前请求信息添加到pendingRequest对象中
      return config;
    },
    (error) => {
      // 发送失败
      console.log(error);
      return Promise.reject(error);
    }
  );
  // 响应拦截
  service.interceptors.response.use(
    (response) => {
      removePendingRequest(response.config); // 从pendingRequest对象中移除请求
      // dataAxios 是 axios 返回数据中的 data
      const dataAxios = response.data;
      // 这个状态码是和后端约定的 根据实际项目修改
      const { status } = dataAxios;
      // 根据 code 进行判断
      if (status === undefined) {
        return dataAxios;
      } else {
        // 有 code 代表这是一个后端接口 可以进行进一步的判断
        switch (status) {
          case 200:
            return dataAxios.data;
          default:
            throw new Error(`${dataAxios.message}: ${dataAxios.data}`);
        }
      }
    },
    (error) => {
      removePendingRequest(error.config || {}); // 从pendingRequest对象中移除请求

      if (axios.isCancel(error)) {
        console.log("已取消的重复请求:" + error.message);
      } else {
        // 添加异常处理
      }
      return Promise.reject(error);
    }
  );
  return service;
}

/**
 * @description 创建请求方法
 * @param {Object} service axios 实例
 */
function createRequestFunction(service) {
  return function(config) {
    const token = localStorage.getItem("token");
    const configDefault = {
      headers: {
        Authorization: "Bearer " + token,
        "Content-Type": get(config, "headers.Content-Type", "application/json"),
      },
      timeout: 60000,
      data: {},
    };
    return service(Object.assign(configDefault, config));
  };
}

// 用于真实网络请求的实例和请求方法
export const service = createService();
export const request = createRequestFunction(service);

http.js

import { request } from "./axios";

/**
 * post请求
 *@param url 请求路径
 *@param data 请求参数
 */
export function httpPost({ url, data = {} }) {
  return request({
    url,
    method: "post",
    data,
  });
}
/**
 * get请求
 *@param url 请求路径
 *@param params 请求参数
 */
export function httpGet({ url, params = {} }) {
  return request({
    url,
    method: "get",
    params,
  });
}
/**
 * put请求
 *@param url 请求路径
 *@param data 请求参数
 */
export function httpPut({ url, data = {} }) {
  return request({
    url,
    method: "put",
    data,
  });
}
/**
 * put请求
 *@param url 请求路径
 *@param data 请求参数
 */
export function httpDelete({ url, data = {} }) {
  return request({
    url,
    method: "delete",
    data,
  });
}

五、测试
import { httpGet } from "../http/http";
export default {
  created() {
    this.getData();
  },
  methods: {
    getData() {
      httpGet({
        url: "http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10",
      }).then((res) => {
        console.log(res);
      });
      httpGet({
        url: "http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10",
      }).then((res) => {
        console.log(res);
      });
      httpGet({
        url: "http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=10",
      }).then((res) => {
        console.log(res);
      });
      httpGet({
        url: "http://127.0.0.1:3000/users/getAll?pageNo=1&pageSize=11",
      }).then((res) => {
        console.log(res);
      });
    },
  },
};

在这里插入图片描述

完工

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值