axios 请求封装及多个请求loading管理

什么是 axios ?

基于 Promise 网络请求库

  • 同构:同一套代码可以运行在浏览器和 node.js 中
  • 在服务端中,使用原生 node.js 的 http 模块
  • 在客户端(浏览器)中,使用 XMLHttpRequest

特性

  • 支持 Promise API
  • 拦截请求和响应
  • 取消请求
  • 自动转换 JSON 数据

一、安装 axios

npm i -S axios qs

二、axios 请求二次封装

src/api/http.js: 

import Vue from 'vue';
import axios from 'axios';
import qs from 'qs';
import router from '../router/index';

let loadingService = null,
  loadingRequestQueue = [];
// 默认接口配置
let initConfig = {
  isNeedLoading: true, // 是否需要loading
  isHideErrorAlert: false, // 是否不显示错误弹窗
};

const instance = axios.create({
  timeout: 40000,
});
// post默认请求头设置
instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';

// 请求拦截
instance.interceptors.request.use(
  config => {
    // console.log('request--->', config);

    const { method = 'get' } = config;
    // config.headers.TrackId = config.trackId;
    // config.headers.CompanyId = 1693;
    if (method === 'post') {
      if (!config.data) {
        config.data = {};
      }
      if (config.submitType === 'json') {
        config.headers['Content-Type'] = 'application/json';
        // config.data = JSON.stringify(config.data) // axios内部自动会序列化
      } else {
        config.data = qs.stringify(config.data);
      }
    }

    return config;
  },
  error => {
    console.log('request error--->', error);
    return Promise.reject(error);
  }
);

// 响应拦截器
instance.interceptors.response.use(
  response => {
    // console.log('response--->', response);
    if (response.status === 200) {
      return Promise.resolve(response);
    }
    return Promise.reject(response);
  },
  error => {
    // console.log('errorRes---->', error.response);
    // console.log('errorMsg---->', error.message);

    const Alert = Vue.prototype.$alert;
    const { response, message = '' } = error;

    // 在这里可以做个收集错误日志操作

    if (response && !response.config?.isHideErrorAlert) {
      if (message.indexOf('timeout') > -1) {
        Alert('请求超时请重试', '异常信息', {
          confirmButtonText: '确定',
          type: 'error',
        });
      }
      if (message.indexOf('Network Error') > -1) {
        Alert('网络出错', '异常信息', {
          confirmButtonText: '确定',
          type: 'error',
        });
      }

      switch (response.status) {
        case 400:
          Alert('400 请求参数有误', '异常信息', {
            confirmButtonText: '确定',
            type: 'error',
          });
          break;

        // 401: 未登录
        // 未登录则跳转登录页面,并携带当前页面的路径
        // 在登录成功后返回当前页面,这一步需要在登录页操作。
        case 401:
          Alert({
            message: '未登录! ',
            type: 'warning',
          });
          router.replace({
            path: '/login',
            query: {
              redirect: router.currentRoute.fullPath,
            },
          });
          break;

        // 403 登录过期
        // 登录过期对用户进行提示
        // 清除本地token和清空vuex中token对象
        // 跳转登录页面
        case 403:
          Alert({
            message: '登录过期,请重新登录',
            type: 'warning',
          });
          setTimeout(() => {
            router.replace({
              path: '/login',
              query: {
                redirect: router.currentRoute.fullPath,
              },
            });
          }, 1000);
          break;
        case 404:
          Alert(response.config?.url + ' 404 Not Found', '异常信息', {
            confirmButtonText: '确定',
            type: 'error',
          });
          break;
        case 405:
          Alert(response.config?.url + ' 405 请求未被允许', '异常信息', {
            confirmButtonText: '确定',
            type: 'error',
          });
          break;
        case 500:
          Alert('500 服务器异常', '异常信息', {
            confirmButtonText: '确定',
            type: 'error',
          });
          break;
        case 501:
          Alert('501 服务器不支持当前请求', '异常信息', {
            confirmButtonText: '确定',
            type: 'error',
          });
          break;
        case 502:
          Alert('502 网关出错', '异常信息', {
            confirmButtonText: '确定',
            type: 'error',
          });
          break;
        case 503:
          Alert('503 服务不可用', '异常信息', {
            confirmButtonText: '确定',
            type: 'error',
          });
          break;
        case 504:
          Alert('504 网关超时', '异常信息', {
            confirmButtonText: '确定',
            type: 'error',
          });
          break;
        default:
          Alert('网络出错', '异常信息', {
            confirmButtonText: '确定',
            type: 'error',
          });
          break;
      }
    }

    return Promise.reject(error);
  }
);

/**
 * 判断是否有接口还未加载完
 * @param {*} trackId
 */
function compareLoadingStatus(trackId) {
  const targetIndex = loadingRequestQueue.findIndex(item => item === trackId);
  if (targetIndex > -1) {
    loadingRequestQueue.splice(targetIndex, 1);
  }
  if (!loadingRequestQueue.length) {
    loadingService?.close();
  }
}

function getTrackId() {
  return `trackid${new Date().getTime()}_${(Math.random() * 100000).toFixed(0)}`;
}

// 请求公共函数
function send(method = 'get', url, data = {}, options = {}) {
  const Loading = Vue.prototype.$loading;
  options = {
    ...initConfig,
    ...options,
    trackId: getTrackId(),
  };

  if (options.isNeedLoading) {
    loadingRequestQueue.push(options.trackId);
    if (!loadingService) {
      loadingService = Loading?.({
        fullscreen: true,
        background: 'transparent',
      });
    }
  }

  return new Promise((resolve, reject) => {
    instance({
      method,
      url,
      [method === 'get' ? 'params' : 'data']: data,
      ...options,
    })
      .then(res => {
        resolve(res.data);
      })
      .catch(error => {
        reject(error);
      })
      .finally(() => {
        if (options.isNeedLoading) {
          compareLoadingStatus(options.trackId);
        }
      });
  });
}

/**
 * 封装get请求
 */
export function get(url, params = {}, options = {}) {
  return send('get', url, params, options);
}

/**
 * 封装post请求
 */
export function post(url, data = {}, options = {}) {
  return send('post', url, data, options);
}

Loading 和 Alert 用的是 element-ui 的。 

options 可选参数:

  • isNeedLoading: boolean 是否需要loading
  • isHideErrorAlert: boolean 请求错误时是否关闭默认的错误提示弹窗
  • submitType: string,值为 json,请求参数会用 json 格式
  • axios 规定的请求配置,如 timeoutwithCredentials

特有功能:

  • 同时发起多个请求时,所有请求结束后才关闭 loading。

请求 headers 里的 Content-Type 可以不用设置,axios 会根据传入的 data 参数的格式自动设置 Content-Type:

data参数格式Content-Type
"name=jim&age=22" 这种序列化过的格式application/x-www-form-urlencoded
普通对象application/json

三、接口管理

api接口管理的一个好处就是,我们把api统一集中起来,如果后期需要修改接口,我们就直接在api.js中找到对应的修改就好了,而不用去每一个页面查找我们的接口然后再修改会很麻烦。关键是,万一修改的量比较大,就gg了。还有就是如果直接在我们的业务代码修改接口,一不小心还容易动到我们的业务代码造成不必要的麻烦。

// api.js
import { get, post } from './http'

export const apiAddress = parmas => post('api/v1/users/info', parmas);

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值