Skip to content
广告位招租

优质广告位诚邀合作

本广告位曝光量正火速攀升,用户触达规模呈爆发式增长!现向品牌开放合作,趁曝光增长红利期,抓紧机会拿下,让您的品牌借势破圈!

--总浏览量(次)
--总计访客(人)

快速开始

阅读此处时,默认您已经完全熟悉axios请求优化包的相关配置,如还未完全了解,请阅读使用须知

安装

shell
npm i vue-axios-optimize -S

建议api接口请求目录如下

src

api

modules

common-api.js

system-api.js

index.js

axios.js

axios-optimize.js

axios.js 为axios相关配置,基础请求路径、请求超时时间、请求拦截与响应拦截的配置文件。

axios-optimize.js 为axios请求优化包配置文件,后续请求方法均需引入此文件。

common-api.js、system-api.js 为通用接口请求方法,如获取用户信息、获取权限信息、获取菜单信息等。

index.js 为api接口请求方法入口文件,引入common-api.js、system-api.js,并导出。

axios.js 配置

请求拦截处需注意的地方:

js

service.interceptors.request.use(
  (config) => {
    // ... 一系列请求前操作
    return config
  },
  (error) => {
     // 此处一定要写成这样, 尤其是return 一定要有
     return Promise.reject(error)
  }
)

响应拦截处需注意的地方:

js
// 对于错误的响应需要

return { ...data, config, responseErr: true }

// 对于正确的响应需要

return { ...data, config, responseErr: true }

下列是vue2-element-admin后台管理系统框架对于响应拦截的相关配置,实现了下载文件名跟随后端配置,详细可阅读前端处理Blob文件下载:实现动态文件名跟随后端配置,实现了权限变更时的弹框提示,同时也解决了当返回类型为文件流且AccessToken过期时的续签配置。以vue2-element-admin为例,对后端的要求就是当请求文件流响应格式的接口报错时,将status-code设置成520,且手动设置一个响应头,其值为401或者403时将错误信息return给vue2-axios-optimize处理。

响应拦截示例:

js
// 具体方法请阅读  前端处理Blob文件下载:实现动态文件名跟随后端配置
import { getFilenameFromContentDisposition } from "@/utils/file"

service.interceptors.response.use(
  async(response) => {
    const { data, config, status, request } = response
    // 如果自定义代码不是200,则判断为错误。
    if (status !== 200) {
      modal.msgError(data.message || "Error")
      return { ...data, config, responseErr: true }
    } else {
      // 二进制数据则直接返回
      if (
        request.responseType === "blob" ||
        request.responseType === "arraybuffer"
      ) {
        // 获取 Content-Disposition 头
        const contentDisposition = response.headers["content-disposition"]
        // 解析文件名
        const fileName = getFilenameFromContentDisposition(contentDisposition) // 默认文件名 为空
        if (fileName) {
          // 存储文件名 用于设置和后端一样的文件名
          localStorage.setItem("downloadFileName", fileName)
        }
        return response
      }
      if (data.code === 200) {
        return { ...data, config }
      }
      if (data.code === 401 || data.code === 403) {
        return { ...data, config, responseErr: true }
      }
      // 用户权限变更
      if (data.code === 402) {
        if (isShowingModal) {
          return { ...data, config, responseErr: true }
        }
        isShowingModal = true
        modal
          .confirm(data.message, {
            type: "warning",
            showClose: false
          })
          .then(async() => {
            // 刷新去首页吧
            window.location.href =
              window.location.origin + window.location.pathname
          })
          .catch(async(err) => {
            // 关闭 MessageBox 后,手动移除 不能关闭的遮罩层
            const maskModal = document.querySelector(".v-modal")
            if (maskModal) {
              maskModal.remove()
            }
            await store.dispatch("user/getUserInfo")
            isShowingModal = false
          })
        return { ...data, config, responseErr: true }
      }
      modal.msgError(data.message || "Error")
      return { ...data, config, responseErr: true }
    }
  },
  (error) => {
    // 加入try 否则 会导致 取消重复请求时 因为无config等字段导致报错 导致取消重复请求失效
    try {
      const { config, headers, status } = error.response
      if (status === 520) {
        // 导出文件接口报错时与后端约定 返回状态码为 520
        // 约定好响应头添加 x-custom-header 且值为原本报错的 code
        const code = Number(headers["x-custom-header"])
        console.log("🚀 ~ code:", code)
        // 当code为401 或者 403时 触发刷新token接口或者弹框提示过期重新登录
        if (code === 401 || code === 403) {
          return {
            code,
            message: "凭证已过期,请重新登录!",
            config,
            responseErr: true
          }
        } else {
          modal.msgError(code === 409 ? "无权限" : error.message || "Error")
          return Promise.reject(error)
        }
      }
    } catch (err) {
      console.log("🚀 ~ err:", err)
    }

    if (error.name !== "CanceledError") { // 非取消请求时 才提示错误
      // 如果是请求超时
      if (error.code === "ECONNABORTED") {
        // 当 不是重发请求或者不是最后一次重发导致的报错时 提示  超时重发版本需要加这个判断
        if (!(error?.config?.currentRetryTimes && error?.config?.maxRetryTimes && error.config.currentRetryTimes < error.config.maxRetryTimes)) {
          modal.msgError(error.message || "Error")
        } 
      } else {
        modal.msgError(error.message || "Error")
      }
    }
    return Promise.reject(error)
  }
)

上述响应拦截配置是相对完整的配置,对于无配置无感续签凭证的配置会相对少很多,切记一个原则就是:

js

const { data, config, status, request } = response

// 对于错误的响应需要

return { ...data, config, responseErr: true }

// 对于正确的响应需要 responseErr 可配置为 false 或者不用配置此字段

return { ...data, config, responseErr: false }

温馨提示

您原先请求拦截处的 error 错误处理一定要加个 return Promise.reject(error),return 关键字很重要 您原先响应拦截处对于需要报错提示的地方,需返回错误信息给优化包:return { ...data, config, responseErr: true } 您原先响应拦截处对于正确的响应,尾部需返回正确数据给优化包:return { ...data, config, responseErr: false } 接口异常响应拦截时,对于取消请求的场景不给予错误消息提示

const { data, config, status, request } = response

axios-optimize.js 配置项

重磅来袭,包的配置来了,直接引入配置代码示例吧

js
import axios from "axios"
import instance from "@/api/axios"
import axiosOptimize from "vue-axios-optimize"
import store from "@/store"
import {MessageBox} from "element-ui"
let isAlertShowing = false // 是否弹出messaBox.alert
// https://www.npmjs.com/package/vue-axios-optimize
const AxiosOptimize = new axiosOptimize(axios, instance, {
  maxReqNum: 2, // 同时最多请求数量 选配  默认 6
  cacheNum: 2, // 缓存数量 选配  默认10
  responseTypesStr: "arraybuffer,blob", // 当axios请求设置 responseType为arraybuffer或blob时,直接返回原始数据 选配 
  showLoadingFun: (config, requestingNum) => { // 当需要加载动画的请求数量不为0时的回调函数
    store.commit("app/SET_LOADING_STATUS", true)
  },
  hideLoadingFun: (config, requestingNum) => { // 当需要加载动画的请求数量为0时的回调函数
    store.commit("app/SET_LOADING_STATUS", false)
  },
  responseResultFun: (res) => { // 响应数据格式化
    return res.data
  },
  openRefresh: true, // 是否开启无感知刷新token 以下配置均与无感知刷新token有关 如无需配置无感知刷新token 可忽略
  code: "code", // 判断响应码的字段标识  默认code
  authorizationKey: "Authorization", // headers存放凭证的字段名 
  accessTokenExpirationCode: 401, // accessToken过期时 接口响应状态码 默认 数字类型 401
  refreshTokenExpirationCode: 403, // 刷新token接口请求报错时的 响应状态码  默认 数字类型 403
  setAuthorizationFun: (config, authorization) => { // 设置接口请求token的 并返回新的config 默认为 config.headers.Authorization = "Bearer " + accessToken
    config.headers.Authorization = authorization
    return config
  },
  getRefreshTokenFun: () => { // 获取refreshToken的方法
    return store.getters.refreshToken
  },
  getAuthorizationFun: () => { // 获取Authorization的方法
    return "Bearer " + store.getters.accessToken
  },
  refreshTokenStore: (refreshToken) => { // 调用刷新token的方法 并将获取到的数据存储到cookie 且需要将接口返回的数据  resolve(data)
    return store.dispatch("user/refreshToken", refreshToken)
  },
  reloginFun: async(response) => { // 刷新token接口也报错后的处理逻辑 建议进行类似如下方法处理
    if (isAlertShowing) {
      return
    }
    isAlertShowing = true
    // 重新登录方法   弹框提示 点击确认后 建议清除 缓存中token等信息后 刷新页面
    MessageBox.alert(`${response.message}`, "提示", {
      confirmButtonText: "OK",
      showClose: false,
      callback: async(action) => {
        isAlertShowing = false
        if (action === "confirm") {
          // 清除缓存中的token等信息
          store.commit("user/CLEAR_AUTH")
          // 刷新浏览器是为了 跳转登录页时query的redirect 会带上 当前页面地址(路由拦截处的逻辑)
          window.location.reload()
        }
      }
    })
  }
})
export default AxiosOptimize

普通请求接口处配置

接口处直接引入五种常用请求方式的使用示例吧:

js
import axiosRequest from '@/api/axios-optimize';
// 需要配置 请求接口时 不加载全局动画 则 配置  noShowLoading: true
// 需要配置 缓存的接口 则 配置  cache: true 否则 配置为 false 或 不配置
// 需要配置 重复请求时 取消前面的请求 则 配置  preventDuplicateRequestsType: "cancel"
// 需要配置 重复请求时 禁用后面的请求 则 配置  preventDuplicateRequestsType: "prevent"
// 需要配置 返回数据是否添加 IS_COMPLETE 是否完成字段 则 配置  showIsComplete: true
// 需要配置 当同时请求很多接口,且还有很多接口在队列时,有接口报错,是否终止所有队列中的接口请求  removeRemainingTasksWhenError: true
// 需要配置 全路经(包括入参数据)为接口唯一标识的  则配置  fullPath: true 否则 配置为 false 或 不配置  否则仅仅以URL为唯一标识
// 续签AccessToken接口需要配置isRefreshToken为true
// get 请求demo
export function getDemo(
  data = {},
  config = {
    noShowLoading: true, // 配置不展示加载动画
    preventDuplicateRequestsType: 'cancel', // 配置重复请求时 取消前面的请求
  },
) {
  return axiosRequest.get(`/xiaobu/getDemo`, { params: data, ...config });
}
// post 请求demo
export function postDemo(data = {}, config = {}) {
  return axiosRequest.post(`/xiaobu/postDemo`, data, config);
}
// delete 请求demo1 使用data作为参数 参数不展示在  请求路径上
export function deleteDemo1(data = {}, config = {}) {
  return axiosRequest.delete(`/xiaobu/deleteDemo1`, { data, ...config });
}
// delete 请求demo2 使用params作为参数 参数展示在  请求路径上
export function deleteDemo2(data = {}, config = {}) {
  return axiosRequest.delete(`/xiaobu/deleteDemo2`, {
    params: data,
    ...config,
  });
}
// put 请求demo
export function putDemo(data = {}, config = {}) {
  return axiosRequest.put(`/xiaobu/putDemo`, data, config);
}
// patch 请求demo
export function patchDemo(data = {}, config = {}) {
  return axiosRequest.patch(`/xiaobu/patchDemo`, data, config);
}

使用时 直接就是

js
getDemo().then()

可中断的接口处配置

可中断的接口配置与普通接口的配置项完全一致,仅在使用时不一样,规则如下:

js
import axiosRequest from '@/api/axios-optimize';
import { createCancelableRequest } from 'vue-axios-optimize/utils.js';

export function getDemo(
  data = {},
  config = {
    noShowLoading: true, // 配置不展示加载动画
    preventDuplicateRequestsType: 'cancel', // 配置重复请求时 取消前面的请求
  },
) {
  return createCancelableRequest(axiosRequest, 'get', `/xiaobu/getDemo`, { params: data, ...config });
}
// post 请求demo
export function postDemo(data = {}, config = {}) {
  return createCancelableRequest(axiosRequest, 'post', `/xiaobu/postDemo`, data, config);
}
// delete 请求demo1 使用data作为参数 参数不展示在  请求路径上
export function deleteDemo1(data = {}, config = {}) {
  return createCancelableRequest(axiosRequest, 'delete', `/xiaobu/deleteDemo1`, { data, ...config });
}
// delete 请求demo2 使用params作为参数 参数展示在  请求路径上
export function deleteDemo2(data = {}, config = {}) {
  return createCancelableRequest(axiosRequest, 'delete', `/xiaobu/deleteDemo2`, {
    params: data,
    ...config,
  });
}
// put 请求demo
export function putDemo(data = {}, config = {}) {
  return createCancelableRequest(axiosRequest, 'put', `/xiaobu/putDemo`, data, config);
}
// patch 请求demo
export function patchDemo(data = {}, config = {}) {
  return createCancelableRequest(axiosRequest, 'patch', `/xiaobu/patchDemo`, data, config);
}

使用时

js
const { cancel, request } = getDemo()
request.then(res => {})
cancel()

超时重发的接口处配置

超时重发接口处配置继承普通接口配置的所有配置项,并且多了widthCancel配置项, 具体如下

js

import axiosRequest from '@/api/axios-optimize';
import { requestWithRetry } from 'vue-axios-optimize/utils.js';

export function getDemo(
  data = {},
  config = {
    noShowLoading: true, // 配置不展示加载动画
    preventDuplicateRequestsType: 'cancel', // 配置重复请求时 取消前面的请求
    timeout: 5000, // 配置请求超时时间 requestWithRetry 方法时 默认为 10000
    widthCancel: true, // 配置是否可主动取消请求  为true时 使用方法不一样 得注意
  },
) {
  return requestWithRetry(axiosRequest, 'get', `/xiaobu/getDemo`, { params: data, ...config });
}
// post 请求demo
export function postDemo(data = {}, config = {}) {
  return requestWithRetry(axiosRequest, 'post', `/xiaobu/postDemo`, data, config);
}
// delete 请求demo1 使用data作为参数 参数不展示在  请求路径上
export function deleteDemo1(data = {}, config = {}) {
  return requestWithRetry(axiosRequest, 'delete', `/xiaobu/deleteDemo1`, { data, ...config });
}
// delete 请求demo2 使用params作为参数 参数展示在  请求路径上
export function deleteDemo2(data = {}, config = {}) {
  return requestWithRetry(axiosRequest, 'delete', `/xiaobu/deleteDemo2`, {
    params: data,
    ...config,
  });
}
// put 请求demo
export function putDemo(data = {}, config = {}) {
  return requestWithRetry(axiosRequest, 'put', `/xiaobu/putDemo`, data, config);
}
// patch 请求demo
export function patchDemo(data = {}, config = {}) {
  return requestWithRetry(axiosRequest, 'patch', `/xiaobu/patchDemo`, data, config);
}

未配置 widthCancel或者配置值为 false 时,使用如下

js
getDemo().then(res => {})

配置 widthCancel值为 true 时,使用如下

js
const { cancel, request } = getDemo()
request.then(res => {})
cancel()

总结

经过上述一系列处理后,我们的配置就完成啦,更多细节及注意事项请继续阅读文档。

微信公众号【爆米花小布】

0%

置顶

置顶