axios二次封装

425 阅读5分钟

创建类

首先我们封装一个YcRequest类,在这个类中我们要实现get,request,delete,patch等方法。为了使我们封装的类更具有扩展性,所以我们选用创建instances的方法来实现上述的方法。

class YcRequest {
  instance: AxiosInstance
  interceptors?: YcRequestinterceptors
  constructor(config: YcResquestConfig) {
    this.instance = axios.create(config)
  }
  request(config: YcResquestConfig) {

  }
  get() {

  }
  delete() {

  }
  patch() {

我们在constror里面创建instance,使得我们没new一个类之后都能有自己的instance.传入的config用来作为create的配置

constructor(config: YcResquestConfig) {
    this.instance = axios.create(config)
  }

然后在到类里面实现一个request方法

  request(config: YcResquestConfig) {
    this.instance.request(config).then((res) => {
      console.log(res);
    })
  }

然后再到出口文件里new一个实例,同时传入基础config (baseURL,timeout)。

const ycrequest = new YcRequest({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
})

再到main.js里应用一下,同时传入config(url和method)。(注意:传入的这些config配置会以一个优先顺序进行合并。这个顺序是:在 lib/defaults.js 找到的库的默认值,然后是实例的 defaults 属性,最后是请求的 config 参数。后者将优先于前者)

YcRequest.request({
  url: '/home/multidata',
  method: 'GET',
})

拦截器

接下来在我们封装的网络请求类中封装我们的拦截器。首先拦截器只会在请求的实例有,并不会出现在config里。例如一下两种方式

axios.interceptors.request.use(function (config) {
    // 在发送请求之前做些什么
    return config;
  }, function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
  });


const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});

为了使我们封装的类更具有扩展性,避免创建出来的实例都能有自己选择性的拦截器,所以我们要做的事就是把interceptors扩展到config里面去。怎么样扩展呢?无非就是在config类的基础上再继承一个拦截器的接口。

interface YcResquestConfig extends AxiosRequestConfig {
  interceptors?: YcRequestinterceptors
}
interface YcRequestinterceptors {
  requestInterceptor?: (value: AxiosRequestConfig) => AxiosRequestConfig
  requestInterceptorCatch?: (error: any) => any
  responseInterceptor?: (value: AxiosResponse) => AxiosResponse
  responseInterceptorCatch?: (error: any) => any
}

config原本的类是AxiosRequestConfig。但是我们可以通过让我们封装的接口YcResquestConfig继承接口AxiosRequestConfig

。我们在自己封装的接口YcResquestConfig中实现了interceptors属性,而这个属性又满足YcRequestinterceptors接口,在那个接口中有拦截器的请求和响应方法,当然这些都是可选的。所以我们通过这种方式来实现了对config属性的扩展,添加了拦截器。

interceptors?: YcRequestinterceptors
constructor(config: YcResquestConfig) {
  this.instance = axios.create(config)
  this.interceptors = config?.interceptors
  //实例的拦截器
  this.instance.interceptors.request.use(this.interceptors?.requestInterceptor,this.interceptors?.requestInterceptorCatch)
this.instance.interceptors.response.use(this.interceptors?.responseInterceptor, this.interceptors?.responseInterceptorCatch)
}

最后我们在constructor中来实现拦截器。先可以把拦截器保存一下放到里面,然后再来现实拦截器方法。

const ycrequest = new YcRequest({
  baseURL: BASE_URL,
  timeout: TIME_OUT,
  interceptors: {
    requestInterceptor: (config) => {
      console.log("请求拦截");
      return config
    },
    requestInterceptorCatch: (err) => {
      return err
    },
    responseInterceptor: (config) => {
      console.log("响应拦截");
      return config
    },
    responseInterceptorCatch: (err) => {
      return err
    }
  }
})

这样我们在用的时候就可以把拦截器当成config传给YcRequest类,这样我们就完成了对于实例的拦截器封装,实例下所有的请求都会带上这些拦截器。

每一个请求的拦截器封装

如果说我们个别的请求不满足于实例封装的拦截器,那我们还可以扩展。例如将传给request的config也从AxiosRequestConfig改为YcResquestConfig。这样我们也可以在传给requestconfig的时候传入拦截器 request(config: YcResquestConfig) { if(config.interceptors?.requestInterceptor) { console.log(config); config = config.interceptors.requestInterceptor(config) console.log(config); } this.instance.request(config).then((res) => { if (config.interceptors?.responseInterceptor) { res = config.interceptors?.responseInterceptor(res) } console.log(res); }) }

request(config: YcResquestConfig) {
    if(config.interceptors?.requestInterceptor) {
      console.log(config);
      config = config.interceptors.requestInterceptor(config)
      console.log(config);
    }
    this.instance.request(config).then((res) => {
      if (config.interceptors?.responseInterceptor) {
        res = config.interceptors?.responseInterceptor(res)
      }
      console.log(res);
    })
  }

首先我们要判断一下传过来的config里面有没有requestInterceptor拦截器。如过有,我们就先把config传给那个requestInterceptor拦截器,做一些操作,做完之后再把config返回。把返回的config传给request,同理再处理responseInterceptor

YcRequest.request({
  url: '/home/multidata',
  method: 'GET',
  interceptors: {
    requestInterceptor: (config) => {return config},
    responseInterceptor: (res) => {return res}
  }
})

在main.js中就是这么使用。

补充一点

如果我们想在创建出来的所有YcRequest实例中都有的拦截器的话,就在constructor中补上一段全局的拦截器

his.instance.interceptors.request.use(config => {
      return config
    },err => {
      return err
    })
    this.instance.interceptors.response.use(config => {
      return config
    }, err => {
      return err
    })

使用elementui里的loading组件的显示和隐藏

我们调用element组件的一个loading加载组件。

import { ElLoading } from 'element-plus'

通过在全局拦截器里调用ElLoading里的services方法同时传入options,这样就能实现loading的效果

this.instance.interceptors.request.use(config => {
      console.log(config);
        this.loadingInstance = ElLoading.service({
          text: '加载中。。。。。',
          background: 'rgba(0,0,0,0.5)'
        })
      return config
    },err => {
      return err
    })

然后在全局响应拦截器里调用close方法来关闭loading

this.instance.interceptors.response.use(config => {
      setTimeout(() => {
        this.loadingInstance?.close()
      }, 2000);
      return config
    }, err => {
      return err
    })

考虑到有一些请求是不需要loading效果的,所以我们要设计在传给request的时候传入一个参数showloading,如下:

YcRequest.request({
  url: '/home/multidata',
  method: 'GET',
  showloading: true 
})
​

要在config里加上一个参数,我们要在YcResquestConfig接口上扩展

interface YcResquestConfig extends AxiosRequestConfig {
  interceptors?: YcRequestinterceptors
  showloading?: Boolean
}

然后在YcRequest类中加上showloading属性。接着在constructor里给showloading赋值,默认为true

this.showloading = config.showloading ?? true

接着在发送请求的时候就可以加上一个判断,如果 showloading的值还是true的话就显示loading效果,否则不显示

if (this.showloading === true ) {
        this.loadingInstance = ElLoading.service({
          text: '加载中。。。。。',
          background: 'rgba(0,0,0,0.5)'
        })
      }

最后一步就是让request里的showloading能控制类里的showloading值。拿到request里的config的showloading值,判断如果是等于false就把类里的showloading的值改为false。这样就完成了通过请求内部的showloading值取消loading动画

if(config.showloading === false) {
      this.showloading = false
}
注意:修改完showloading之后,在下一个请求中showloading默认还是false,所以我们要在一个请求完之后修改showloading的值为true

总结:

1 为什么要用类封装而不是函数封装?

类的可扩展性强,封装性强,可以把很多函数封装到类里,继承什么的都会好很多

2 为什么可以自己扩展接口

不满足于给定的接口 我们可以设计一个自己的接口,这个接口可以继承原先给定的接口,同时又可以满足我们给定的东西例如这个拦截器/