Vue-vben-adminVue3+TSAxios的封装源码分析
Vue-vben-admin Vue3+TS Axios的封装源码分析
前⾔
⼀、近期再⽤Vue3+TS 重构之前Vue2的项⽬,因此想着借鉴⼀下业界较为优秀的代码,在Git上⾯了好久,经过同事推荐,我发现由anncwd出品的Vue-vben-admin是很不错的,截⽌⽬前在git上⾯已经有5.9k个star了,⽬前将这个模板看完了,我觉得⾥⾯写的最好的莫过于对于Axios的封装了,在此我简单对这个源码进⾏⼀个分析,⾥⾯也涉及⼀些TS+Vue3的知识点,最重要的是,能够发现其中写的特别优秀的地⽅,我们也可以运⽤在⾃⼰的项⽬当中。
1.我们先来看⼀下源码中的⽬录结构吧
2.⽬录分析,这个⽂件夹当中分为6个⽂件,其中index.ts是⼊⼝⽂件,Axios是主要的对于axios⼆次封装的类,axiosCancel是对于取消请求类canceler的封装,axiosTransform.ts是定义了⼀个类,涵盖所有对于数据处理⽆论错误还是失败的钩⼦函数,checkStatus.ts是对于后端返回code⾮200时的处理函数,helper是⼀个处理时间的函数,这6个⽂件相辅相成,构建了⼀个强⼤的axios的⼆次封装,接下来
我们就来看⼀看其中每个⽂件的源码吧
⼆、index.ts
1.在看index.ts的源码之前我必须先带着⼤家补充⼏个知识点,以免其中有⼏个地⽅⽐较卡顿
(1)ts中的Partial<T>类,实际上在ts当中为我们提供了这样的⼀个类,Partial
它可以将接受⼀个T泛型,可以将T中的所有属性变为可选的,换句话说,任意类型的机构,经过Partial处理后,所有的内部属性就变为可选的了。
interface A{
a:string
b:number
}
const a:A={// error 因为A类型必须具备两个属性
a:'张三'
}
const b:Partial<A>={// successful 经过Partial处理后,所有属性值变为可选
a:'张三'
}
//看⼀下他的源码
type Partial<T>={
[P in keyof T]?:T[P];
};
//其实就是给每⼀个属性加了⼀个?操作符⽽已,将所有属性变为⾮必须的了⽽已
(2)encodeURIComponent
//这个是JavaScript的原⽣Api ,可以把字符串作为 URI 组件进⾏编码。字母数字保持原来的样⼦,当时汉字或者其他所有符号都会将其编码为数字,字母或者%,
var uri="/my test.php?name=ståle&car=saab";
encodeURIComponent(uri)
// 输出:http%3A%%2Fmy%20test.php%3Fname%3Dst%C3%A5le%26car%3Dsaab
有了上⾯的补充,接下来就直接来看⼀下代码吧,我都做了注释,但是下⾯的代码会依赖⼀些类型或者函数,⼤家先过⼀下,我们主要看⼀下结构,开始吧
import{ AxiosTransform }from'./axiosTransform'
import{ AxiosResponse, AxiosRequestConfig }from'axios'
import{ Result, RequestOptions, CreateAxiosOptions }from'./types'
import{ errorResult }from'./const'
import{ ResultEnum, RequestEnum, ContentTypeEnum,}from'../../../enums/httpEnum'
import{ useMessage }from'@/hooks/web/useMessage'
import{ isString, isObject, isBlob }from'@/utils/is'
import{ formatRequestDate }from'./helper'
import{ setObjToUrlParams, deepMerge }from'@/utils'
import{ getToken }from'@/utils/auth'
import{ checkStatus }from'./checkStatus'
import VAxios from'./Axios'
const{ createMessage, createErrorModal }=useMessage()
/**
* 请求处理
*/
const transform: AxiosTransform ={// 所谓transform 本质上就是 transform 这个对象中拥有多个处理数据的钩⼦/**
* 请求成功处理
* 但依然要根据后端返回code码进⾏判断
*/
transformRequestData:(// 对后端返回的数据做处理,这是在http状态码为200的时候
res: AxiosResponse<Result>,
options: RequestOptions
)=>{
const{ isTransformRequestResult }= options
if(!isTransformRequestResult)return res.data // 不处理直接返回res.data
const{ data }= res
if(!data)return errorResult // 错误处理
const{ code, msg: message }= data
if(code === ResultEnum.UNLOGIN){
const msg ='请重新登陆!'
<(msg)
return errorResult
}
if(code === ResultEnum.BACKERROR){
const msg ='操作失败,系统异常!'
<(msg)
return errorResult
}
const hasSuccess =
data && Reflect.has(data,'code')&& code === ResultEnum.SUCCESS// 200
if(!hasSuccess){// ⾮200 ⾮ 401 ⾮500 的情况直接返回后端提⽰的错误
if(message){
MessageMode ==='modal'){
createErrorModal({ title:'错误提⽰', content: message })
}else{
<(message)
}
}
return errorResult
}
if(code === ResultEnum.SUCCESS)return data // 200 返回data
if(code === ResultEnum.TIMEOUT){
const timeoutMsg ='登录超时,请重新登录!'
createErrorModal({
title:'操作失败',
content: timeoutMsg,
})
return errorResult
return errorResult
}
return errorResult
},
/*
* 请求发送之前的钩⼦说⽩了,本质上这个函数就是在处理发送之前的参数⽤户永远只需要传params就可以传参数了
*/
beforeRequestHook:(config: AxiosRequestConfig, options: RequestOptions)=>{
const{ apiUrl, joinParamsToUrl, formatDate }= options
if(apiUrl &&isString(apiUrl)){
config.url =`${apiUrl}${config.url}`
}
hod?.toUpperCase()=== RequestEnum.GET){// 对get⽅法做处理,避免浏览器缓存数据,导致数刷新不及时const now =new Date().getTime()
if(!isString(config.params)){
config.params = Object.assign(config.params ||{},{ _t: now })
}else{
config.url = config.url +'/'+encodeURIComponent(config.params)
config.params =undefined
}
}else{
// 这个是post 或者其他的⾮get⽅式的固定写法必须将参数放在data当中
if(!isString(config.params)){
formatDate &&formatRequestDate(config.params)
if(joinParamsToUrl){
config.url =setObjToUrlParams(config.url as string, config.params)
}else{
config.data = config.params
}
config.params =undefined
}else{
config.url = config.url +'/'+encodeURIComponent(config.params)
config.params =undefined
}
}
return config
},
// 请求拦截,添加token 没什么说的
requestInterceptors:(config)=>{
const token =getToken()error parse new
if(token){
config.headers.Authorization = token
}
return config
},
// 当http状态码⾮200时的错误处理
responseInterceptorsCatch:(error:any)=>{
//todo
const{ response, code, message }= error ||{}
const err:string= String()
try{
if(code ==='ECONNABORTED'&& message.indexOf('timeout')!==-1){
<('接⼝请求超时,请刷新页⾯重试!')
}
if(err && err.includes('Network Error')){
createErrorModal({
title:'⽹络异常',
content:'⽹络异常,请检查您的⽹络连接是否正常!',
})
}
}catch(error){
throw new Error(error)
}
let msg =`服务器发⽣异常!`
/
/ 这是处理http状态码发⽣异常时的可能,使⽤服务器返回的msg提⽰,但状态取决于http状态码
if(
response &&
response.data &&
isObject(response.data)&&// 假如data是个⼀般对象,说明可以判断
de === ResultEnum.ERROR
){
msg = response.data.msg
sponse && sponse.status, msg)
}else if(response && response.data &&isBlob(response.data)){
const text =new FileReader()
const obj =JSON.sult as string)
msg = de === ResultEnum.ERROR? obj.msg :'服务器发⽣异常!'
sponse && sponse.status, msg)
}
}else{
// 使⽤默认的message提⽰
sponse && sponse.status, msg)
}
ject(error)
},
}
function createAxios(opt?: Partial<CreateAxiosOptions>){// 把CreateAxiosOptions 中的每个属性变为可选的
return new VAxios(// 这个http会返回⼀个Vaxios的实例对象在不传任何参数的情况下默认传递以下的参数
deepMerge(
{
timeout:6*10*1000,
headers:{'Content-type': ContentTypeEnum.JSON},
transform,
requestOptions:{
isTransformRequestResult:true,//是否转换结果
joinParamsToUrl:false,//是否将参数添加到url
formatDate:true,//是否格式化时间参数
errorMessageMode:'none',//错误消息的提⽰模式
apiUrl: v.VUE_APP_API_URL,//api前缀
},
},
opt ||{}
)
)
}
export const http =createAxios()
我⼤概总结⼀下这个index到底做了什么
1. 导出了⼀个函数的调⽤,实际上是导出了⼀个VAxios的实例对象,并且 默认情况下传递了⼀⼤堆的参数,参数就是这些东西
{
timeout:6*10*1000,
headers:{'Content-type': ContentTypeEnum.JSON},
transform,
requestOptions:{
isTransformRequestResult:true,//是否转换结果
joinParamsToUrl:false,//是否将参数添加到url
formatDate:true,//是否格式化时间参数
errorMessageMode:'none',//错误消息的提⽰模式
apiUrl: v.VUE_APP_API_URL,//api前缀
},
}
后⾯它会根据这些参数的值,来决定如何处理数据,我们最重要还是要看⼀下,transform的内容,这个是重点。
2. tansform 这个是⼀个对象,⾥⾯实际上包含了多个钩⼦函数,请求前处理数据的 beforeRequestHook,请求
requestInterceptors添加token,请求成功后的transformRequestData,也就是对于响应成功回来的时候如何返回数据,因为有的时候我们需要返回整个response,⽐如下载⽂件的时候,有的时候只需要返回response.data就好了,responseInterceptorsCatch ,当http状态码⾮200时的错误处理 。通俗来讲,可以理解为,transform对象中就是⼀堆钩⼦函数,⽤于处理数据。或者根据数据情况来做不同逻辑的操作。
3. 最终实际上这个时候我们就知道要去看看VAxios到底是个什么东西了,也就是最核⼼的⽂件。
⼀起来看⼀下
⼆、Axios.ts
import axios,{ AxiosInstance, AxiosRequestConfig, AxiosResponse }from'axios'
import{
CreateAxiosOptions,
UploadFileParams,
RequestOptions,
Result,
}from'./types'
import{ isFunction }from'@/utils/is'
import{ ContentTypeEnum }from'../../../enums/httpEnum'
import{ cloneDeep }from'lodash-es'
import{ errorResult }from'./const'
import{ AxiosCanceler }from'./axiosCancel'
export default class VAxios {
private axiosInstance: AxiosInstance // axios实例本⾝
private readonly options: CreateAxiosOptions // 传递进来的 options
constructor(options: CreateAxiosOptions){
this.options = options
this.axiosInstance = ate(options)
this.setupInterceptors()// 将当前实例添加响应拦截
}
private createAxios(config: CreateAxiosOptions):void{// 更新实例
this.axiosInstance = ate(config)
}
getAxios(): AxiosInstance {
return this.axiosInstance
}
//重新配置axios
configAxios(config: CreateAxiosOptions){
if(!this.axiosInstance){
return
}
}
setHeader(headers:any):void{
if(!this.axiosInstance)return
Object.assign(this.axiosInstance.defaults.headers, headers)
}
private getTransform(){
const{ transform }=this.options
return transform
}
/**
* 配置
*/
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论