vue2/3 axios 请求重试、取消请求、loading 串行并行等(分享)

基础版,添加 loading

在请求响应拦截器里面添加 loading,这样就不需要给每一个请求添加 loading 了
这些代码都是 vue2 项目的,vue3 也通用,改一下 loading 和 message 就好了(主要是 element 的区别)
我这里最后没有合并代码,有的配置不适合写在一起,看自己项目的需要

import axios from 'axios';
import { Loading, Message } from 'element-ui';

const instance = axios.create({
  baseURL: 'http://localhost:5500',
  timeout: 30000,
});

let loadingInstance = null;
// 添加请求拦截器
instance.interceptors.request.use(
  (config) => {
    loadingInstance = Loading.service({
      fullscreen: true,
      text: '加载中...',
      background: "black",
    });
    return config;
  },
  // 断网走这里
  (error) => {
    Message.error('请求错误:' + error.message);
    return Promise.reject(error);
  },
);

// 添加响应拦截器
instance.interceptors.response.use(
  ({ data }) => {
    loadingInstance.close();
    return data;
  },
  // !== 2xx 走这里
  (error) => {
    loadingInstance.close();
    return Promise.reject(error.response);
  },
);

export default instance

响应状态码配置

响应状态码配置,更好的给用户提示,这里只设计到响应,就只写了响应拦截

// 根据规范添加配置
const statusOption = [
  {
    code: "200",
    state: "success",
    description: "请求(或处理)成功",
  },
  {
    code: "400",
    state: "ParameterError",
    description: "请求参数不完整或不正确",
  },
  {
    code: "401",
    state: "Unauthorized",
    description: "访问请求未授权! 当前 SESSION 失效, 请重新登陆",
  },
  {
    code: "403",
    state: "Forbidden",
    description: "您无权进行此操作,请求执行已拒绝",
  },
  {
    code: "404",
    state: "NotFound",
    description: "找不到与请求匹配的 HTTP 资源",
  },
  {
    code: "405",
    state: "HttpMehtodError",
    description: "HTTP请求类型不合法",
  },
  {
    code: "406",
    state: "HttpRequestError",
    description: "HTTP请求不合法,请求参数可能被篡改",
  },
  {
    code: "407",
    state: "URLExpireError",
    description: "该URL已经失效",
  },
  {
    code: "500",
    state: "Error",
    description: "内部请求出错",
  },
];

// 添加响应拦截器
instance.interceptors.response.use(
  ({ data }) => {
    loadingInstance.close();
    // 这里是请求成功的内部状态码
    // 这个要和后端约定,我这里就默认除了 200 都有问题
    // 这样就不用在请求接口出错的时候一个个 message 了
    if (data.status !== 200) {
      Message.error(data.message);
      return Promise.reject(data);
    }
    return Promise.resolve(data);
  },
  (error) => {
    let status = error.response.status;
    if (status < 400) {
      Message.warning(error.response.statusText);
    } else {
      let err = statusOption.find((item) => item.code == status);
      Message.error(err ? err.description : "响应出错请联系管理员!");
    }
    loadingInstance.close();
    return Promise.reject(error.response);
  }
);

loading 串行、并行

串行 loading 闪屏、并行 loading 提前关闭

  • 串行的情况是请求之间的依赖,请求 A 完成后立即开始请求 B,这样会导致 loading 闪屏
  • 并行是两个请求同时请求,请求 A 的时间为 5s,请求 B 的时间为 3s,因为 loading 都是同一个实例,会导致请求 B 时提前关闭 loading,关闭两秒后又请求到了新的数据
import axios from "axios";
import { Loading, Message } from 'element-ui';

const instance = axios.create({
  baseURL: "http://localhost:5500",
  timeout: 30000,
});

// 处理并行
let loadingCount = 0;

// 处理串行
let loadingTimer = null;

function loadingClose() {
  loadingCount--;
  if (!loadingCount) {
    // 延迟等待是否还有下一个请求
    loadingTimer = setTimeout(() => {
      loadingInstance.close();
      loadingTimer = null;
    }, 300);
  }
}

function loadingOpen() {
  loadingCount++;
  // 如果有请求需要清除关闭
  if (loadingTimer) {
    clearTimeout(loadingTimer);
    loadingTimer = null;
  }
  loadingInstance = Loading.service({
    fullscreen: true,
    text: "加载中...",
    background: "black",
  });
}

let loadingInstance = null;
// 添加请求拦截器
instance.interceptors.request.use(
  (config) => {
    loadingOpen();
    return config;
  },
  (error) => {
    Message.error("请求错误:" + error.message);
    return Promise.reject(error);
  }
);

// 添加响应拦截器
instance.interceptors.response.use(
  ({ data }) => {
    loadingClose();
    return data;
  },
  (error) => {
    loadingClose();
    return Promise.reject(error.response);
  }
);

export default instance;

请求挂起

延迟请求发送
我这里的应用场景是 token,当我发现当前请求没有 token 时,先挂起当前请求然后获取 token,在添加上

import axios from "axios";
import { Loading, Message } from 'element-ui';

const instance = axios.create({
  baseURL: "http://localhost:5500",
  timeout: 30000,
});

// 获取 token 的方法,这里的请求不能使用封装的,不然请求一直没有 token 就会一直挂起
async function passParamsGetToken() {}

let loadingInstance = null;
// 添加请求拦截器
instance.interceptors.request.use(
  // 这种用 async/await 也可以,更易读一些,原理都是等待请求的发送
  (config) => {
    if (!config.headers.Authorization) {
      // 请求挂起
      return new Promise(async (resolve, reject) => {
        let token = await passParamsGetToken();
        // 为当前请求添加 token
        config.headers.Authorization = token;
        // 为实例添加 token,这样下一个请求也会存在token
        instance.defaults.headers.common["Authorization"] = token;

        loadingInstance = Loading.service({
          fullscreen: true,
          text: "加载中...",
          background: "black",
        });
        resolve(config);
      });
    } else {
      loadingInstance = Loading.service({
        fullscreen: true,
        text: "加载中...",
        background: "black",
      });
      return config;
    }
  },
  (error) => {
    Message.error("请求错误:" + error.message);
    return Promise.reject(error);
  }
);

// 添加响应拦截器
instance.interceptors.response.use(
  ({ data }) => {
    loadingInstance.close();
    return data;
  },
  (error) => {
    loadingInstance.close();
    return Promise.reject(error.response);
  }
);

export default instance;

取消请求

重复请求需要取消,取消请求会走响应拦截器的 error,会和请求重试有冲突

  • 取消请求不是真正意义上的取消,请求已经发送,后端接收到了,只是前端不在响应了
  • 使用表单类的操作,取消请求没用,顶多是获取列表的时候,前端少处理一次操作
import axios from "axios";
import { Loading, Message } from 'element-ui';

const requestMap = new Map();

function setRequestMap(config) {
  requestMap.set(JSON.stringify(config), config);
}

function deleteRequestMap(config) {
  let xLConfig = JSON.stringify(config);
  let hasConfig = null;
  if ((hasConfig = requestMap.get(xLConfig))) {
    // 请求完成后在取消,不影响
    hasConfig.controller.abort();
    requestMap.delete(xLConfig);
  }
}

// 设置控制器,需要先设置控制器,这样添加删除后的序列化字符才可以匹配
function setAbortController(config) {
  const controller = new AbortController();
  config.controller = controller;
  config.signal = controller.signal;
}

const instance = axios.create({
  baseURL: "http://localhost:5500",
  timeout: 30000,
});

let loadingInstance = null;
// 添加请求拦截器
instance.interceptors.request.use(
  (config) => {
    setAbortController(config);
    deleteRequestMap(config);
    setRequestMap(config);

    loadingInstance = Loading.service({
      fullscreen: true,
      text: "加载中...",
      background: "black",
    });
    return config;
  },
  (error) => {
    Message.error("请求错误:" + error.message);
    return Promise.reject(error);
  }
);

// 添加响应拦截器
instance.interceptors.response.use(
  (response) => {
    deleteRequestMap(response.config);
    loadingInstance.close();
    return response.data;
  },
  (error) => {
    // 重复请求的取消,表示后面肯定有请求,则不需要关闭 loading
    if (axios.isCancel(error)) {
      // console.log("Request canceled", 123123);
      // 其他异常的处理
    } else {
      deleteRequestMap(error.config);
      loadingInstance.close();
    }
    return Promise.reject(error.response);
  }
);

export default instance;

请求重试

请求失败需要重试

// utils/reuqest.js 中
import axios from "axios";
import { Loading, Message } from 'element-ui';

const instance = axios.create({
  baseURL: "http://localhost:5500",
  timeout: 30000,
  retry: 3, // 最大重试次数为 3
  retryDelay: 1000, // 重试时的延迟时间为 1 秒
});

let loadingInstance = null;
// 添加请求拦截器
instance.interceptors.request.use(
  (config) => {
    loadingInstance = Loading.service({
      fullscreen: true,
      text: config.retryCount ? "请求重试中..." : "加载中...", // 重试后改变 text 显示
      background: "black",
    });
    return config;
  },
  (error) => {
    Message.error("请求错误:" + error.message);
    return Promise.reject(error);
  }
);

// 添加响应拦截器
instance.interceptors.response.use(
  ({ data }) => {
    loadingInstance.close();
    return data;
  },
  (error) => {
    loadingInstance.close();
    const config = error.config;
    // 没有重试的请求直接抛出
    if (!config || !config.retry) {
      return Promise.reject(error);
    }

	// 超过重试次数抛出
    config.retryCount = config.retryCount || 0;
    if (config.retryCount >= config.retry) {
      // 因为每次请求的实例对象都不一样,所以为当前实例添加 retryCount,不会影响其他实例
      return Promise.reject(error);
    }

	// 请求重试
    config.retryCount += 1;
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve(instance(config));
      }, config.retryDelay);
    });
  }
);

export default instance;
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是在Vue3中使用axios进行接口调用时请求头的书写方法: ```javascript import axios from 'axios' // 创建axios实例 const service = axios.create({ baseURL: process.env.VUE_APP_BASE_API, // api的base_url timeout: 5000 // 请求超时时间 }) // request拦截器 service.interceptors.request.use( config => { // 在发送请求之前做些什么 config.headers['Content-Type'] = 'application/json;charset=UTF-8' config.headers['Authorization'] = 'Bearer ' + getToken() // 如果需要携带token的话 return config }, error => { // 对请求错误做些什么 console.log(error) // for debug Promise.reject(error) } ) // response拦截器 service.interceptors.response.use( response => { // 对响应数据做点什么 return response.data }, error => { // 对响应错误做点什么 console.log('err' + error) // for debug return Promise.reject(error) } ) export default service ``` 在上述代码中,我们首先通过`import`语句导入了`axios`模块,并使用`axios.create()`方法创建了一个axios实例。然后,我们通过`service.interceptors.request.use()`方法和`service.interceptors.response.use()`方法分别添加了请求拦截器和响应拦截器,以便在请求和响应时进行一些处理。在请求拦截器中,我们设置了请求头的`Content-Type`和`Authorization`字段,以便在发送请求时携带相应的信息。在响应拦截器中,我们对响应数据进行了处理,并返回了响应数据的`data`字段。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值