TS+vue 中的axios 封装

1,303 阅读4分钟

axios 项目封装使用

1.安装依赖

使用npm

$ npm install axios

使用 bower:

$ bower install axios

使用 cdn:

<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

2.封装

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
export interface CommonResult <T> {
  code: number;
  msg: string;
  result: T;
  _info?: CommonResult<T>;
}
interface LhHttpRequestConfig extends AxiosRequestConfig {
  noErrorMessage?: boolean; // 是否自动输出错误Message
  sync?: boolean; // 该接口是否合并节流(同时触发多次时取最后一次)TODO 暂时不写,方案在优化中
}
class HttpRequest {
  // 请求实例 - 单例
  // tslint:disable-next-line:variable-name
  private static _instance: HttpRequest;
  private static API_PREFIX: string = '';
  private static stamps: {[key: string]: number} = {};

  // tslint:disable-next-line:variable-name
  private _axiosInstance!: AxiosInstance;

  constructor(config?: LhHttpRequestConfig) {
    // 单例输出
    if (HttpRequest._instance) {
      return HttpRequest._instance;
    }
    // 初始化
    this._init(config);
  }

  public async request<T>(url: string, config: LhHttpRequestConfig, mock: boolean = false) {
    config.url = HttpRequest.API_PREFIX + (config.url || url);
    // 只对开发环境进行处理
    if (process.env.NODE_ENV === 'development') {
      // 开启mock
      if (mock) {
        Object.assign(config, {
          url: '/mock' + config.url,
        });
      } else {
        Object.assign(config, {
          url: '/dev' + config.url,
        });
      }
    }
    const { data } = await this._axiosInstance.request<CommonResult<T>>(config);
    return data;
  }

  // get !!!
  public async get<T>(url: string, config: LhHttpRequestConfig, mock: boolean = false) {
    config.url = HttpRequest.API_PREFIX + (config.url || url);
    // 只对开发环境进行处理
    if (process.env.NODE_ENV === 'development') {
      // 开启mock
      if (mock) {
        Object.assign(config, {
          url: '/mock' + config.url,
        });
      } else {
        Object.assign(config, {
          url: '/dev' + config.url,
        });
      }
    }
    const { data } = await this._axiosInstance.get<CommonResult<T>>(url, config);
    return data;
  }
  // post !!!
  public async post<T>(url: string, config: LhHttpRequestConfig, mock: boolean = false) {
    config.url = HttpRequest.API_PREFIX + (config.url || url);
    // 只对开发环境进行处理
    if (process.env.NODE_ENV === 'development') {
      // 开启mock
      if (mock) {
        Object.assign(config, {
          url: '/mock' + config.url,
        });
      } else {
        Object.assign(config, {
          url: '/dev' + config.url,
        });
      }
    }
    const { data } = await this._axiosInstance.post<CommonResult<T>>(url, config);
    return data;
  }

  // 初始化axios
  private _init(config: AxiosRequestConfig = {
    baseURL: '', // api的base_url
    method: 'post', // 默认是 post
    headers: {
      'Content-Type': 'application/json;charset=UTF-8',
    },
  }) {
    // 创建axios实例
    this._axiosInstance = axios.create(config);
    this.addRequestIpt();
    this.addResponseIpt();

    HttpRequest._instance = this;
  }

  // 添加请求拦截器
  private addRequestIpt() {
    // 添加请求拦截器
    this._axiosInstance.interceptors.request.use(
      (config: AxiosRequestConfig) => {
        // 在发送请求之前做些什么
        // 公共参数
        const commonParams = {};
        // 混入公共参数
        if (config.data) {
          config.data = Object.assign(config.data, commonParams);
        }

        // 标记response按照请求顺序返回
        if (config.data && config.data.isSync) {
          /*
            生成一个时间戳,绑定到url,一个存到到全局变量,另一个添加到请求配置中并随response返回
            然后拿response返回的时间戳与全局变量中的去比对,如果不一致,则添加一个为按请求顺序返回的标记
          */
          const time = Date.now();
          HttpRequest.stamps = HttpRequest.stamps || {};
          HttpRequest.stamps[config.url as string] = time;
        }
        return config; // 此处切记记得将请求参数return出去
      },
      (error: AxiosRequestConfig) => {
        // 对请求错误做些什么
        // 下放错误
        Promise.reject(error);
      },
    );
  }

  // 添加请求拦截器
  private addResponseIpt() {
    // 添加响应拦截器
    this._axiosInstance.interceptors.response.use(
      (response: AxiosResponse<any>) => {
        const { data: { code, msg }, config } = response;
        // 是否是二进制文件流
        // 对一些错误的code 的处理,比如登录过期等等
        if (code === 100012) {
          // xxxx
        }

        // 对响应数据做点什么
        if (response.data.status === '403') {
          // router.push('/login')
        }

        // 只将response 中的 data 输出
        return response;
      },
      /**
       * 下面的注释为通过response自定义code来标示请求状态,当code返回如下情况为权限有问题,登出并返回到登录页
       * 如通过httpRequest 状态码标识 逻辑可写在下面error中
       */
      (err) => {
        // 对响应错误做点什么
        if (err && err.response) {
          switch (err.response.status) {
            case 400:
              err.message = '(错误请求) 服务器不理解请求的语法。';
              break;
            case 401:
              err.message =
                '(未授权) 请求要求身份验证。 对于需要登录的网页,服务器可能返回此响应。';
              break;
            case 403:
              err.message = '(禁止) 服务器拒绝请求。';
              break;
            case 404:
              err.message = `(未找到) 服务器找不到请求的网页。${err.response.config.url}`;
              break;
            case 408:
              err.message = '(请求超时) 服务器等候请求时发生超时。';
              break;
            case 500:
              err.message = '(服务器内部错误) 服务器遇到错误,无法完成请求。';
              break;
            case 501:
              err.message =
                '(尚未实施) 服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码。';
              break;
            case 502:
              err.message =
                '(错误网关) 服务器作为网关或代理,从上游服务器收到无效响应。';
              break;
            case 503:
              err.message =
                '(服务不可用) 服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态。';
              break;
            case 504:
              err.message =
                '(网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。';
              break;
            case 505:
              err.message =
                '(HTTP 版本不受支持) 服务器不支持请求中所用的 HTTP 协议版本。';
              break;
            default:
          }
        }
        // 可在此注入log操作 !!!!!
        // 下放错误
        return Promise.reject(err);
      },
    );
  }
}

拆解

1.HttpRequest 类封装了axios 实现了初始化默认参数,axios 请求拦截器,响应拦截器。

2.快捷实现了 httpRequest.request 方法 get,post 方法

3.扩展了AxiosInstance 可以多传一个noErrorMessage 控制是否在拦截器中默认输出错误

3.调用

api目录

import http, { CommonResult } from '@/axios/http-request.ts';
interface Result {
  code: number;
  msg: string;
}

// 测试get 客户列表
interface GetCustomerListParams {
  keyword?: string;
  gender?: 1 | 2;
  age_from?: number;
  age_to?: number;
  has_mobile?: 0 | 1 | -1;
  has_face?: 0 | 1 | -1;
  page?: number;
  per_page?: number;
}
interface CustomerRes {
  data: any; link: any; meta: any; _info?: Result;
}
export async function getCustomerList(params: GetCustomerListParams, mock: boolean = false ): Promise<CommonResult<CustomerRes>> {
    const  data  = await http.request<CustomerRes>('/business/customers', {
      method: 'get',
      params,
    }, mock);
    return data;
}

// 这是一个比较强约束的一个api方法实现,当然你大可不必完全按照上面的TypeScript约束

App.vue

export default class App extends Vue {

  public async created() {
    console.log(HttpRequest);
    const res = await getCustomerList({}, true);
    res.result.data;
  }
}

此处ts 的提示已经非常齐全