划分代码文件结构
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) })
网络异常,图片无法展示
|