Vue3 Axios的封装

99 min read
import router from '@router';
import store from '@store';
import AxiosInstance from 'axios';
import { Loading, Message } from 'element-ui';

import { AUTH_ERRORS, ERRORS } from '../utils/errors';

const qs = require("qs");

let loadingInstance = null;

const showLoading = () => {
  loadingInstance = Loading.service({ fullscreen: true });
};

const hideLoading = () => {
  if (loadingInstance) {
    loadingInstance.close();
    loadingInstance = null;
  }
};

const handlerError = error => {
  if (ERRORS[error.code]) {
    return Message({
      message: error.message,
      type: "error",
      center: true,
      onClose: () => { }
    });
  }

  if (AUTH_ERRORS[error.code]) {
    router.replace("login");
    return;
  }
};

// token拦截器
const tokenInterceptor = config => {
  const token = store.getters["auth/token"];
  if (token) {
    config.headers = {
      ...config.headers,
      Authorization: `Bearer ${token}`
    };
  }
  return config;
};

// 格式化参数拦截器
const paramsInterceptor = config => {
  config.paramsSerializer = params => {
    return qs.stringify(params, {
      arrayFormat: "brackets",
      encode: false
    });
  };
  return config;
};

// 显示loading拦截器
const loadingInterceptor = config => {
  showLoading();
  return config;
};

// 正在pending中的请求
const pending = {};
const CancelToken = axios.CancelToken;
const removePending = (config, cb) => {
  // 确保请求和相应的url相同
  const url = config.url.replace(config.baseURL, "/");
  // 使用URL参数将整个RESTful请求字符串化
  const flagUrl = `${url}&${config.method}&${JSON.stringify(config.params)}`;

  if (flagUrl in pending) {
    if (typeof cb === "function") {
      cb();
    } else {
      delete pending[flagUrl];
    }
  } else {
    if (typeof cb === "function") {
      pending[flagUrl] = cb;
    }
  }
};

// cancelToken拦截器
const cancelTokenInterceptor = config => {
  config.cancelToken = new CancelToken(c => {
    removePending(config, c);
  });
  return config;
};

const createInstance = (
  options = { showLoading: false, hideLoading: true },
  config = {}
) => {
  // 默认配置
  const defaultConfig = {
    baseURL: process.env.VUE_APP_BASE_URL || "/",
    timeout: 30000,
    headers: {
      // 'X-Requested-With': 'XMLHttpRequest',
      Accept: "application/json",
      "Content-Type": "application/json"
      // Authorization: `Bearer ${localStorage.getItem('token')}`,
      // post: {
      //   'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
      // }
    },
    ...config
  };

  const httpClient = AxiosInstance.create(defaultConfig);
  httpClient.interceptors.request.use(cancelTokenInterceptor);
  httpClient.interceptors.request.use(tokenInterceptor);
  httpClient.interceptors.request.use(paramsInterceptor);

  if (options.showLoading) {
    httpClient.interceptors.request.use(loadingInterceptor);
  }

  httpClient.interceptors.response.use(
    res => {
      if (res.config.responseType === "blob" && res.data) {
        return res.data;
      }
      if (options.hideLoading) {
        hideLoading();
      }
      if (!res.data) return res;
      // if (+res.data.code !== 200) {
      //   handlerError(res.data);
      //   return Promise.reject(res.data);
      // }
      return res.data.data ? res.data.data : res.data;
    },
    err => {
      removePending(err.config);

      if (!AxiosInstance.isCancel(err)) {
        return Promise.reject(err);
      }

      if (options.hideLoading) {
        hideLoading();
      }

      // if (err.message.includes("timeout")) {
      //   handlerError({ code: 0 });
      //   return Promise.reject({ code: 0 });
      // }

      let { status, statusText, data, headers, request } = err.response;
      handlerError(data);
      return Promise.reject(err.response);
    }
  );

  httpClient.clear = () => {
    Object.keys(pending).map(e => {
      if (typeof pending[e] === "function") {
        pending[e]();
        delete pending[e];
      }
    });
  };

  return httpClient;
};

export const axios = createInstance();
export const axiosLoading = createInstance({
  showLoading: true,
  hideLoading: true
});
export const getAxios = createInstance;
export default axios;