目前前端最流行的网络请求库还是axios,所以对axios的封装很有必要,此次基于vue3+ts的环境下。

axios的基本使用

import axios from 'axios'
// console.log('adh')
axios.get('http://XXX.xxx.xxx.xxx:8000/home').then((res) => {
  console.log(res.data)
})

axios.get()会返回一个Promise对象,所以可以用.then获取返回的数据。

axios.all()方法

axios.all([
  axios.get('http://httpbin.org/get').then((res) => {
    console.log(res.data)
  }),
  axios.post('http://httpbin.org/post').then((res) => {
    console.log(res.data)
  })
])

axios一些基本配置

在axios中,有一些默认配置,它们是存在于axios.defaults中的,比如我们经常会用到的baseURL、timeout属性

axios.defaults.baseURL = 'http://httpbin.org'
axios.defaults.timeout = 10000

axios的拦截器

在平常的使用中,我们经常需要对axios请求进行拦截以处理一些特殊情况,如获取token、处理异常等,这时候我们就需要使用axios的拦截器()。

axios.interceptors.request.use(
  (config) => {
    return config
  },
  (error) => {
    return error
  }
)
axios.interceptors.response.use(
  (res) => {
    console.log(res.data)
  },
  (error) => {
    return error
  }
)

如上,axios.interceptors.request是对请求的拦截,此时可以为请求添加headers,添加token等操作,二axios.interceptors.response则是对请求的返回进行拦截,在此处我们可以统一返回的数据结构以及对错误进行一些统一的处理

封装axios-封装基础属性

首先,我们先确认一下基本思路,我们把主要的逻辑封装成一个类,然后编写一个出口文件,将该类导出,需要注意的是,往这个类中传入哪些参数,诚然,我们可以直接在类中定义诸如BASE_URL、timeout、interceptors等axios的属性或方法,但是为了今后的可适配性更高,我们应该尽量的把可配置的属性作为变量传入我们欲封装的类中,下面先进行一个基本的封装:

// 封装类
import axios from 'axios'
import { AxiosInstance, AxiosRequestConfig } from 'axios'
class ZWRequest {
  instance: AxiosInstance
  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config)
  }

  request(config: AxiosRequestConfig): void {
    this.instance.request(config).then((res) => {
      console.log(res) // 此处能成功打印出结果代表成功
    })
  }
}
export default ZWRequest
// 出口文件index.ts
import ZWRequest from './request'
import { BASE_URL, timeout } from './request/config'

const zwRequest = new ZWRequest({
  baseURL: BASE_URL, // 配置参数
  timeout: timeout // 配置参数
})
export default zwRequest
// 在main.ts中测试
import ZWRequest from './service/index.ts'
ZWRequest.request({
  url: '/post',
  method: 'POST'
})

封装拦截器

上面的封装可以传入BASE_URL、timeout等一些基础属性,但是对于拦截器interceptors还是不能实现配置,所以下一步我们改造一下使其可以传入拦截器:

// 封装类
import axios from 'axios'
import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios'

// 自建一个用于匹配interceptors的类型
interface ZWRequestInterceptors {
  requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig // 这是个函数类型
  requestErrorInterceptor?: (error: any) => any
  responseInterceptor?: (config: AxiosResponse) => AxiosResponse
  responseErrorInterceptor?: (error: any) => any
}
/** 再新建一个一个类型继承AxiosRequestConfig类型,并在其中设立一个属性,
该属性对应上一步建立的类型,如此,我们就可以用这个类型来代替封装类
的constructor()函数传入的参数类型了,在此基础上,完成对拦截器参数的传入。*/
interface ZWRequestConfig extends AxiosRequestConfig {
  interceptors?: ZWRequestInterceptors
}
class ZWRequest {
  instance: AxiosInstance
  interceptors?: ZWRequestInterceptors
  constructor(config: ZWRequestConfig) {
    this.instance = axios.create(config)
    this.interceptors = config.interceptors
    this.instance.interceptors.request.use(
      this.interceptors?.requestInterceptor,
      this.interceptors?.requestErrorInterceptor
    )
    this.instance.interceptors.response.use(
      this.interceptors?.responseInterceptor,
      this.interceptors?.responseErrorInterceptor
    )
  }

  request(config: ZWRequestConfig): void {
    this.instance.request(config).then((res) => {
      console.log(res)
    })
  }
}
export default ZWRequest
// 出口函数index.ts
/** 我们可以在出口函数中同意规定拦截器的形式以及相应的处理,这样做的好处是如果我们想
再生成一个可用的axios对象,如ZWRequest2,而且想实现与ZWRequest不一样的拦截方法,那么
就只需要在该页面再新创建一个对象即可 */
import ZWRequest from './request'
import { BASE_URL, timeout } from './request/config'

const zwRequest = new ZWRequest({
  baseURL: BASE_URL,
  timeout: timeout,
  interceptors: {
    requestInterceptor: (config) => {
      console.log('发送请求成功11', config)
      return config
    },
    responseInterceptor: (res) => {
      console.log('返回成功11', res)
      return res
    }
  }
})
const zwRequest2 = new ZWRequest({
  baseURL: BASE_URL,
  timeout: timeout,
  interceptors: {
    requestInterceptor: (config) => {
      console.log('发送请求成功22', config)
      return config
    },
    responseInterceptor: (res) => {
      console.log('返回成功22', res)
      return res
    }
  }
})
export default zwRequest
// main.ts中实验
ZWRequest.request({
  url: '/post',
  method: 'POST'
})
ZWRequest2.request({
  url: '/post',
  method: 'POST'
})

封装公用的拦截器

上面的封装中,拦截器是由每个实例传入的,但是有时候我们就是想所有的实例都拥有共同的拦截器,那么我们就需要在axios封装类里面添加共有的拦截器了(实例传入的拦截器也并不会被取消),只需要在axios封装类中添加以下代码即可实现全局的拦截:

this.instance.interceptors.request.use(
      (config) => {
        console.log('共有的请求时成功拦截')
        return config
      },
      (error) => {
        console.log('共有的请求时失败拦截')
        return error
      }
    )
this.instance.interceptors.response.use(
  (res) => {
    console.log('共有的返回时成功的拦截')
    return res
  },
  (error) => {
    console.log('共有的返回时失败的拦截')
    return error
  }
)

对单个请求传入拦截器

其实上面对拦截器的封装已经基本可以满足平时的开发需求了,但是如果你想更灵活些,比如每个请求都可以传入自己的拦截器,那么请往下看,如果我们需要再请求时传入拦截器,那么就需要看看我们是怎么调用的。目前,我们采用ZWRequest.request(config)的方式调用axios请求,很显然,在封装类中,config参数的类型是:AxiosRequestConfig,这个类型很显然不能传入拦截器参数。

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

所以为了能够往request方法中传入拦截器参数,我们需要将AxiosRequestConfig类型化成我们上面新建立的类型ZWRequestConfig,这样就可以从单个请求处传入各自的拦截器了。

// 改造axios封装类中的request方法
request(config: ZWRequestConfig): void {
    // 对单独请求传来的拦截器进行处理
    if (config.interceptors?.requestInterceptor) {
      config = config.interceptors?.requestInterceptor(config)
    }
    this.instance.request(config).then((res) => {
      if (config.interceptors?.responseInterceptor) {
        res = config.interceptors?.responseInterceptor(res)
      }
      console.log(res)
    })
}
// 在main.ts中进行测试
ZWRequest.request({
  url: '/post',
  method: 'POST',
  interceptors: {
    requestInterceptor: (config) => {
      console.log('单独请求的请求成功拦截')
      return config
    },
    responseInterceptor: (res) => {
      console.log('单独请求的响应成功拦截')
      return res
    }
  }
})

可以正常打印,成功!

对request请求方法封装

上面其实已经对request请求进行了大部分的封装了,但是此时的各种返回还局限在类里面,我们在main.ts中是无法拿到的,那么想要拿到返回值,我们就需要进一步操作,其实就是利用promise将结果返回出来:

// 对request方法的改造
request<T>(config: ZWRequestConfig): Promise<T> {
  return new Promise((resolve, reject) => {
    // 对单独请求传来的拦截器进行处理
    if (config.interceptors?.requestInterceptor) {
      config = config.interceptors?.requestInterceptor(config)
    }
    if (config.showLoading === false) {
      // 代表该请求不想显示加载动画
      this.showLoading = config.showLoading
    }
    this.instance
      .request<any, T>(config)
      .then((res) => {
        // 每次请求返回后将showLoading的值改为默认值,以免被这次请求穿的配置影响下一次请求的加载动画显示
        this.showLoading = DEFAULT_LOADING
        if (config.interceptors?.responseInterceptor) {
          res = config.interceptors?.responseInterceptor(res)
        }
        resolve(res)
        console.log(res)
      })
      .catch((error) => {
        console.log(error)
        this.showLoading = DEFAULT_LOADING
        reject(error)
      })
  })
}
// 在main.ts中实验
interface dataType {
  data: any
  returnCode: string
  success: boolean
}
ZWRequest.request<dataType>({
  url: '/home/multidata',
  method: 'GET'
  // showLoading: false
}).then((res) => {
  console.log(res.data)
  console.log(res.returnCode)
  console.log(res.success)
})

由上可见,其实操作很简单,那么接下来即使利用已经写好的request方法来些各种常用的请求调用方法了,就不多做赘述了,代码如下:

get<T>(config: ZWRequestConfig): Promise<T> {
return this.request<T>({ ...config, method: 'GET' })
}
post<T>(config: ZWRequestConfig): Promise<T> {
  return this.request<T>({ ...config, method: 'POST' })
}
delete<T>(config: ZWRequestConfig): Promise<T> {
  return this.request<T>({ ...config, method: 'DELETE' })
}

以上就是本人对axios的全部封装,完毕!

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐