33Vue-项目实训(三)

100 阅读6分钟

axios的封装

序言

用来发送网络请求,如果每个模块都要发送axios请求,是会有很多弊端的。
每个模块对axios的依赖性太强,一旦axios库出现了问题,不再维护,例如浏览器升级了、webpack中存在不兼容,那么我这么多模块全部都会出问题。并且我又不是必须要用axios,如果后续有更好的第三方库,我难道还要对每一个模块进行修改吗?这样耦合度太高了,会非常的麻烦。
并且发生网络请求的时候,我们会有很多公共的特性,比如说请求时携带token,我需要进行存放在header中,如果我直接使用axios的话,那我每一次axios请求,都要存放一次token。所以我直接做一个封装,将携带token写入进去,会更加的方便。类似的还有很多重复的共性。
综合考虑:我们不要对axios有太多的依赖,我们在工具模块中对他进行封装,其他地方再使用的时候,就可以使用封装的工具了。只有封装的库会对axios有依赖,只需要在封装的库这一个文件中,修改代码,其他的模块代码不需要修改,大大减少了我们后期的维护。

封装过程

1.建立service的统一出口/service/index.ts
避免命名冲突:封装的名字前面加一些属于自己的特征。名字缩写前缀
用来导出所有封装好的逻辑代码的 一个统一出口

import HYRequest from './request'
const hyRequest = new HYRequest()
//声明封装类并导出
export default hyRequest{
    
}

2./service/request/index.ts
将真正的逻辑代码写入在其中

为什么封装成类,而不是函数,因为函数一个一个导出麻烦,并且类具有更强的封装性。
通过类创建出来的对象,直接调用里面的函数。

class HYRequest{
    //封装request(){}函数
    //封装get(){}函数
}

export default HYRequest

3.main.ts中使用

import hyRequest from './service'
hyRequest.request()
hyRequest.get()

baseURL的不同

我定义的baseURL可能会有很多不同的,但是axios.defaults.baseURL,通过axios的defaults只能设置一个 我们一般会再new一个axios实例:

//两个axios不同的实例,但是我怎么能确定这是两个不同的axios实例
const hyRequest = new HYRequest({
    //要求传入constructor中config中的参数,有很多
    baseURL:BASE_URL
    //到底是哪个baseURL,vue会进入到config.ts中进行匹配,因为baseURL从那里导出的
})

const hyRequest2 = new HYRequest({
    baseURL:BASE_URL
})

export default hyRequest,hyRequest2

引入axios时,其实每个axios都是一个实例,所以我们可以通过构造器来进行改进,instance相当于axios
/service/request/index.ts

import axios from 'axios'
import type {AxiosInstance} from 'axios'
import { AxiosRequestConfig } from 'axios'
class HYRequest {
    //最终创建出自己的实例,会保存在instance中,类型必须是这个
    instance: AxiosInstance
    //config代表:基本的配置,也有属于他的类型AxiosRequestConfig
    constructor(config: AxiosRequestConfig){
    //这样我每次新创建一个HYRequest的时候,都会通过axios的create方法创建config,也就是实例,赋值给instance
        this.instance = axios.create(config)
    }
}

import type 是用来协助进行类型检查和声明的,在运行时是完全不存在的
config:存放baseURL、params、url等等...

图片.png

对于配置来说,我们要先创建实例,才能设置配置。对于实例中的方法来说(request),我们创建完实例后,可以直接调用,然后再调用的时候进行配置。

图片.png

封装request函数

/service/request.index

import axios from 'axios'
import { AxiosInstance } from 'axios'
import { AxiosRequestConfig } from 'axios'

class HYRequest {
  instance: AxiosInstance
  constructor(config: AxiosRequestConfig) {
    this.instance = axios.create(config)
  }
  //封装request函数,类型和创建实例的类型是一样的
  //因为我请求,确实就是要请求这些数据,修改也确实要修改请求中存在的数据
  request(config:AxiosRequestConfig):void{
      this.instance.request(config).then((res)=>{
          console.log(res) 
      })
  }
}

export default HYRequest

main.ts中使用

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

封装实例拦截器

在拦截器中添加token、loading等
但是每个请求,拦截的东西是不一样的,不过大部分都是一样的。
比如说:两个不同的实例,第一个只拦截添加token、第二个只拦截添加loading,他们拦截的目的是不一样的
我们希望做到扩展性更强,就可以采取如下写法
/service/index.ts

import HYRequest from "./request"
import { BASE_URL } from "./request/config"

const hyrequest = new HYRequest({
  baseURL:BASE_URL,
  /*
  //除了这些基本的,我还希望传入一个拦截器
      但是config哪有拦截器属性?我们需要进行改进
  */
  interceptors:{
      requestInterceptor:(config)=>{
          return config
      },
      requestInterceptorCatch:(err)=>{
          return err
      },
      responseInterceptor:(res)=>{
          return res
      },
      responseInterceptorCatch:(err)=>{
          return err
      }
  }
  
})

export default hyrequest

/service/request/index.ts

import axios from 'axios'
import type {AxiosInstance,AxiosRequestConfig,AxiosResponse} from 'axios'
//定义一个接口
interface HYRequestInterceptors{
//想定义为接口类型,就要传入对应的拦截器,然后将拦截器保存在自己相应的实例中
//要求传入一个函数,并且有参有返回值(请求成功的拦截),参数就是config中的属性,因为我要对他进行拦截修改
//参数是AxiosRequestConfig类型,返回值是AxiosRequestConfig
    requestInterceptor?:(config: AxiosRequestConfig) => AxiosRequestConfig
    
//请求失败的拦截    
    requestInterceptorCatch?:(err:any) => any

//响应成功的拦截
    responseInterceptor?:(res:AxiosResponse) => AxiosResponse

//响应失败的拦截
    responseInterceptorCatch?:(err:any) => any
    
}

//我自定义一个接口继承AxiosRequestConfig,这样我就可以自己加入一些自定义的属性了
//这样我constructor中的config类型就可以被我自定义的接口所替代,就可以使用hooks(拦截器)了
interface HYRequestConfig extends AxiosRequestConfig{
    //将interceptors设置为可选的,我在请求响应的时候甚至可以不用拦截,不是必传的
    interceptors?:HYRequestInterceptors
}

class HYRequest{
    instance: AxiosInstance
    interceptors?:HYRequestConfig
    
//constroctor(config:AxiosRequestConfig)
    //替换后config的类型
    constroctor(config:HYRequestConfig){
        this.instance = axios.create(config)
        //拦截器赋值保存
        this.interceptors = config.interceptors
        this.instance.interceptors.request.use(
        //我的requestInterceptor可能是没有值的哦
            this.interceptors?.requestInterceptor,
            this.interceptors?.requestInterceptorCatch            
        )
        this.instance.interceptors.response.use(
            this.interceptors?.responseInterceptor,
            this.interceptors?.responseInterceptorCatch
        )
    }
}

axios.interceptors.request.use() 拦截请求
axios.interceptors.response.use() 拦截响应 ---------------------------------------------改进 request/type.ts:存放自定义接口

import type {AxiosRequestConfig,AxiosResponse} from 'axios'

interface HYRequestInterceptors{
//想定义为接口类型,就要传入对应的拦截器,然后将拦截器保存在自己相应的实例中
//要求传入一个函数,并且有参有返回值(请求成功的拦截),参数就是config中的属性,因为我要对他进行拦截修改
//参数是AxiosRequestConfig类型,返回值是AxiosRequestConfig
    requestInterceptor?:(config: AxiosRequestConfig) => AxiosRequestConfig
    
//请求失败的拦截    
    requestInterceptorCatch?:(err:any) => any

//响应成功的拦截
    responseInterceptor?:(res:AxiosResponse) => AxiosResponse

//响应失败的拦截
    responseInterceptorCatch?:(err:any) => any
    
}

export interface HYRequestConfig extends AxiosRequestConfig{
    //将interceptors设置为可选的,我在请求响应的时候甚至可以不用拦截,不是必传的
    interceptors?:HYRequestInterceptors
}

/request/index

import axios from 'axios'
import type { AxiosInstance,AxiosRequestConfig } from 'axios'
import type { HYRequestInterceptors,HYRequestConfig } from './type'
class HYRequest{
    instance: AxiosInstance
    interceptors?:HYRequestInterceptors
    
    constructor(config: HYRequestConfig){
        this.instance = axios.create(config)
        this.interceptors = config.interceptors
        
        //从config中取出的拦截器是对应的实例的拦截器
        this.instance.interceptors.request.use(
            this.interceptors?.requestInterceptor,
            this.interceptors?.requestInterceptorCatch
        )
        this.instance.interceptors.response.use(
            this.interceptors?.responseInterceptor,
            this.interceptors?.responseInterceptorCatch
        )
    }
    request(config:AxiosRequestConfig):void{
        this.instance.request(config).then((res)=>{
            console.log(res)
        })
    }
}

图片.png

一般都是一个实例,一个实例对应一个服务器的地址。
当我有多个服务器时,根路径就会不同,就需要创建多个实例,这个时候我们封装的axios就起作用了
其他的实例使用拦截器的时候,只会用到自己的,因为我new出来的是自身的,然后通过实例在创建自身的拦截器。

图片.png

封装实例拦截器