划分代码文件结构
config
: 导出一些全局变量type
: 定义一个类型接口index
: axios的封装文件
config文件
// 配置请求超时时间,
// 根据不同环境来改变baseUrl
let BASE_URL = ''
const TIME_OUT = 5000
if (process.env.NODE_ENV === 'development') {
BASE_URL = 'development'
} else if (process.env.NODE_ENV === 'production') {
BASE_URL = 'production'
} else {
BASE_URL = 'test'
}
export { BASE_URL, TIME_OUT }
type文件
如果我们不封装axios,我们直接使用axios提供的类型即可。但是我们需要提供一个拦截器函数,并且还需要展示loading,所以我们需要扩展一些内置的接口。
import type { AxiosRequestConfig, AxiosResponse } from 'axios'
// 定义传入的拦截器接口,并且都是可以可选的。
interface IRequestInterceptors<T = AxiosResponse> {
// 请求成功时的拦截器
requestSuccessInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
// 请求失败时的拦截器
requestErrorInterceptor?: (err: any) => any
// 响应成功时的拦截器
responseSuccessInterceptor?: (res: T) => T
// 响应失败时的拦截器
responseErrorInterceptor?: (err: any) => any
}
// 这个接口将要代替AxiosRequestConfig
export interface IRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
// 每个request实例可以不传入拦截器
interceptors?: IRequestInterceptors<T>
// 是否显示loading
showLoading?: boolean
}
index文件
我们通过类来对axios封装,使其可以创建多个axios实例。有自己独特的状态。
定义axios的全局拦截器
他的目的是,每个axios实例和请求都需要做的操作,我们可以放在全局拦截器中。此时的拦截器我们在构造函数中定义其逻辑即可。
import axios, { AxiosInstance } from 'axios'
import { IRequestConfig } from './type'
class Request {
public instance: AxiosInstance
constructor(config: IRequestConfig) {
this.instance = axios.create(config)
// 创建全局请求拦截器
this.instance.interceptors.request.use(
(config) => {
console.log('全局请求成功创建的拦截器')
return config
},
(err) => {
console.log('全局请求失败创建的拦截器')
return err
}
)
// 创建全局响应拦截器
this.instance.interceptors.response.use(
(config) => {
console.log('全局响应成功创建的拦截器')
return config
},
(err) => {
console.log('全局响应失败创建的拦截器')
return err
}
)
}
}
定义axios的实例拦截器
这些拦截器其实和全局一样,如果没有创建多个axios实例的时候。大部分情况下,我们是不需要创建多个axios实例的。这时候我们扩展的AxiosRequestConfig
接口就派上用场了。这些拦截器我们就需要在创建实例的时候传入对应的逻辑。然后再构造函数中注册即可.
import axios, { AxiosInstance } from 'axios'
import { IRequestConfig } from './type'
class Request {
public instance: AxiosInstance
constructor(config: IRequestConfig) {
this.instance = axios.create(config)
// 创建实例请求拦截器
this.instance.interceptors.request.use(
config.interceptors?.requestSuccessInterceptor,
config.interceptors?.requestErrorInterceptor
)
// 创建实例请求拦截器
this.instance.interceptors.response.use(
config.interceptors?.responseSuccessInterceptor,
config.interceptors?.responseErrorInterceptor
)
}
}
封装request
函数
其实这个就是调用axios中的 request
函数做二次封装。
我们知道promise的优势,所以我们将request返回一个promise对象。但是promise需要传入一个泛型,作为返回值的类型。所以我们调用request函数的时候需要传入一个返回值的类型。并且我们可以在这里创建单个请求独有的拦截器。这些拦截器需要在调用request
函数的时候传入对应的逻辑。并且调用传入的拦截器,并将其返回值给配置对象或者返回值。
// 传入的泛型是约束返回值
request<T>(config: IRequestConfig<T>): Promise<T> {
return new Promise((resolve, reject) => {
// 创建单个请求的请求拦截器
if (config.interceptors?.requestSuccessInterceptor) {
// 直接调用,然后将处理后的config返回
config = config.interceptors.requestSuccessInterceptor(config)
}
this.instance
.request<any, T>(config)
.then((res) => {
// 调用传入的响应拦截器
if (config.interceptors?.responseSuccessInterceptor) {
res = config.interceptors.responseSuccessInterceptor(res)
}
resolve(res)
})
.catch((err) => {
reject(err)
})
})
}
封装其他的请求方法。
这些方法都是借助上面封装的request
方法。
get<T>(config: IRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'GET' })
}
post<T>(config: IRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'POST' })
}
delete<T>(config: IRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'DELETE' })
}
patch<T>(config: IRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'PATCH' })
}
通过element-plus来加载loading
loading组件,我们可以通过element-plus中的el-loading来加载。并且以服务的方式进行调用。注意新版本的类型导入路径。
import { ILoadingInstance } from 'element-plus/lib/components/loading'
import axios, { AxiosInstance } from 'axios'
import { IRequestConfig } from './type'
import { ElLoading } from 'element-plus/lib/components'
// 默认加载loading
const DEFAULT_LOADING = true
// 定义两个属性
public showLoading: boolean
public loadingInstance?: ILoadingInstance
constructor(config: IRequestConfig) {
// 默认不加载loading
this.showLoading = config.showLoading ?? DEFAULT_LOADING
this.instance = axios.create(config)
// 创建全局请求拦截器
this.instance.interceptors.request.use(
(config) => {
console.log('全局请求成功创建的拦截器')
// 当showLoading为true是加载loading
if (this.showLoading) {
// 添加加载loading
this.loadingInstance = ElLoading.service({
text: '正在加载,请稍等...',
background: 'rgba(0, 0, 0, .1)',
lock: true
})
}
return config
},
(err) => {
console.log('全局请求失败创建的拦截器')
// 请求错误,让loading关闭
this.loadingInstance?.close()
return err
}
)
// 创建全局响应拦截器
this.instance.interceptors.response.use(
(config) => {
console.log('全局响应成功创建的拦截器')
// 响应时,让loading关闭
this.loadingInstance?.close()
return config
},
(err) => {
console.log('全局响应失败创建的拦截器')
// 响应出错时,让loading关闭
this.loadingInstance?.close()
return err
}
)
}
// 传入的泛型是约束返回值
request<T>(config: IRequestConfig<T>): Promise<T> {
return new Promise((resolve, reject) => {
// 定制该请求是否加loading。当为传入该参数时,默认为true
if (config.showLoading === false) {
this.showLoading = false
}
// 创建单个请求的请求拦截器
if (config.interceptors?.requestSuccessInterceptor) {
// 直接调用,然后将处理后的config返回
config = config.interceptors.requestSuccessInterceptor(config)
}
this.instance
.request<any, T>(config)
.then((res) => {
// 单个请求结束后,让其loading等于默认值。因为我们将showLoading挂载到Request实例上,状态一直被修改
this.showLoading = DEFAULT_LOADING
// 调用传入的响应拦截器
if (config.interceptors?.responseSuccessInterceptor) {
res = config.interceptors.responseSuccessInterceptor(res)
}
resolve(res)
})
.catch((err) => {
// 单个请求结束后,让其loading等于默认值。因为我们将showLoading挂载到Request实例上,状态一直被修改
this.showLoading = DEFAULT_LOADING
reject(err)
})
})
}
完整的index文件代码
import { ILoadingInstance } from 'element-plus/lib/components/loading'
import axios, { AxiosInstance } from 'axios'
import { IRequestConfig } from './type'
import { ElLoading } from 'element-plus/lib/components'
const DEFAULT_LOADING = true
class Request {
public instance: AxiosInstance
public showLoading: boolean
public loadingInstance?: ILoadingInstance
constructor(config: IRequestConfig) {
// 默认不加载loading
this.showLoading = config.showLoading ?? DEFAULT_LOADING
this.instance = axios.create(config)
// 先创建实例请求拦截器
this.instance.interceptors.request.use(
config.interceptors?.requestSuccessInterceptor,
config.interceptors?.requestErrorInterceptor
)
// 先创建实例请求拦截器
this.instance.interceptors.response.use(
config.interceptors?.responseSuccessInterceptor,
config.interceptors?.responseErrorInterceptor
)
// 创建全局请求拦截器
this.instance.interceptors.request.use(
(config) => {
console.log('全局请求成功创建的拦截器')
if (this.showLoading) {
// 添加加载loading
this.loadingInstance = ElLoading.service({
text: '正在加载,请稍等...',
background: 'rgba(0, 0, 0, .1)',
lock: true
})
}
return config
},
(err) => {
console.log('全局请求失败创建的拦截器')
this.loadingInstance?.close()
return err
}
)
// 创建全局响应拦截器
this.instance.interceptors.response.use(
(config) => {
console.log('全局响应成功创建的拦截器')
setTimeout(() => {
this.loadingInstance?.close()
}, 3000)
return config
},
(err) => {
console.log('全局响应失败创建的拦截器')
this.loadingInstance?.close()
return err
}
)
}
// 传入的泛型是约束返回值
request<T>(config: IRequestConfig<T>): Promise<T> {
return new Promise((resolve, reject) => {
// 定制该请求是否加loading。当为传入该参数时,默认为true
if (config.showLoading === false) {
this.showLoading = false
}
// 创建单个请求的请求拦截器
if (config.interceptors?.requestSuccessInterceptor) {
// 直接调用,然后将处理后的config返回
config = config.interceptors.requestSuccessInterceptor(config)
}
this.instance
.request<any, T>(config)
.then((res) => {
this.showLoading = DEFAULT_LOADING
// 调用传入的响应拦截器
if (config.interceptors?.responseSuccessInterceptor) {
res = config.interceptors.responseSuccessInterceptor(res)
}
resolve(res)
})
.catch((err) => {
this.showLoading = DEFAULT_LOADING
reject(err)
})
})
}
get<T>(config: IRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'GET' })
}
post<T>(config: IRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'POST' })
}
delete<T>(config: IRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'DELETE' })
}
patch<T>(config: IRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'PATCH' })
}
}
export default Request
测试
import Request from './http/request'
import { BASE_URL, TIME_OUT } from './http/request/config'
const request = new Request({
baseURL: BASE_URL,
timeout: TIME_OUT,
showLoading: true,
interceptors: {
requestSuccessInterceptor(config) {
console.log('Request实例请求成功的拦截器')
return config
},
requestErrorInterceptor(err) {
console.log('Request实例请求失败的拦截器')
return err
},
responseSuccessInterceptor(res) {
console.log('Request实例响应成功的拦截器')
return res
},
responseErrorInterceptor(err) {
console.log('Request实例响应失败的拦截器')
return err
}
}
})
interface IRequestData {
data: any
}
request
.get<IRequestData>({
url: 'search?keywords=海阔天空',
showLoading: true,
interceptors: {
requestSuccessInterceptor(config) {
console.log('get请求的拦截器')
return config
}
}
})
.then((res) => {
console.log('res ====', res)
})