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等等...
对于配置来说,我们要先创建实例,才能设置配置。对于实例中的方法来说(request),我们创建完实例后,可以直接调用,然后再调用的时候进行配置。
封装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)
})
}
}
一般都是一个实例,一个实例对应一个服务器的地址。
当我有多个服务器时,根路径就会不同,就需要创建多个实例,这个时候我们封装的axios就起作用了
其他的实例使用拦截器的时候,只会用到自己的,因为我new出来的是自身的,然后通过实例在创建自身的拦截器。