目录
前言
本篇来封装一个axios,这部分很重要。
1、createAxios函数
新建/@/utils/http/index.ts文件,这里面用来存放核心的createAxios函数。
// /@/utils/http/index.ts
function createAxios(opt?: Partial<CreateAxiosOptions>) {
return new VAxios(
// 深度合并
deepMerge(
{
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication#authentication_schemes
// authentication schemes,e.g: Bearer
// authenticationScheme: 'Bearer',
authenticationScheme: '',
timeout: 10 * 1000,
// 基础接口地址
// baseURL: globSetting.apiUrl,
headers: {
'Content-Type': ContentTypeEnum.JSON },
// 如果是form-data格式
// headers: { 'Content-Type': ContentTypeEnum.FORM_URLENCODED },
// 数据处理方式
transform: clone(transform),
// 配置项,下面的选项都可以在独立的接口请求中覆盖
requestOptions: {
// 默认将prefix 添加到url
joinPrefix: true,
// 是否返回原生响应头 比如:需要获取响应头时使用该属性
isReturnNativeResponse: false,
// 需要对返回数据进行处理
isTransformResponse: true,
// post请求的时候添加参数到url
joinParamsToUrl: false,
// 格式化提交参数时间
formatDate: true,
// 消息提示类型
errorMessageMode: 'message',
// 接口地址
apiUrl: globSetting.apiUrl,
// 接口拼接地址
urlPrefix: urlPrefix,
// 是否加入时间戳
joinTime: true,
// 忽略重复请求
ignoreCancelToken: true,
// 是否携带token
withToken: true,
retryRequest: {
isOpenRetry: true,
count: 5,
waitTime: 100,
},
},
},
opt || {
},
),
);
}
export const defHttp = createAxios();
有很多爆红,下面逐个解析。
1.1、CreateAxiosOptions类型
新建/@/utils/http/axios/axiosTransform.ts文件作为数据处理类。
// /@/utils/http/axios/axiosTransform.ts
/**
* 数据处理类,可根据项目配置
*/
import type {
AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import type {
RequestOptions, Result } from '/#/axios';
export interface CreateAxiosOptions extends AxiosRequestConfig {
authenticationScheme?: string;
transform?: AxiosTransform;
requestOptions?: RequestOptions;
}
export abstract class AxiosTransform {
/**
* @description: 请求前的流程配置
*/
beforeRequestHook?: (config: AxiosRequestConfig, options: RequestOptions) => AxiosRequestConfig;
/**
* @description: 处理响应数据
*/
transformResponseHook?: (res: AxiosResponse<Result>, options: RequestOptions) => any;
/**
* @description: 请求失败处理
*/
requestCatchHook?: (e: Error, options: RequestOptions) => Promise<any>;
/**
* @description: 请求之前的拦截器
*/
requestInterceptors?: (
config: InternalAxiosRequestConfig,
options: CreateAxiosOptions,
) => InternalAxiosRequestConfig;
/**
* @description: 请求之后的拦截器
*/
responseInterceptors?: (res: AxiosResponse<any>) => AxiosResponse<any>;
/**
* @description: 请求之前的拦截器错误处理
*/
requestInterceptorsCatch?: (error: Error) => void;
/**
* @description: 请求之后的拦截器错误处理
*/
responseInterceptorsCatch?: (axiosInstance: AxiosResponse, error: Error) => void;
}
从axiosTransform数据处理类里面可以看到后续要封装的各种函数处理。
接着去全局类型里补充缺失的类型,新建/@/types/axios.d.ts文件。
// /@/types/axios.d.ts
export type ErrorMessageMode = 'none' | 'modal' | 'message' | undefined;
export type SuccessMessageMode = ErrorMessageMode;
export interface RequestOptions {
// 将请求参数拼接到url
joinParamsToUrl?: boolean;
// 格式化请求参数时间
formatDate?: boolean;
// 是否处理请求结果
isTransformResponse?: boolean;
// 是否返回本机响应标头
// 例如:当您需要获取响应标头时,请使用此属性
isReturnNativeResponse?: boolean;
// 是否加入url
joinPrefix?: boolean;
// 接口地址,如果保留为空,请使用默认的apiUrl
apiUrl?: string;
// 请求拼接路径
urlPrefix?: string;
// 错误消息提示类型
errorMessageMode?: ErrorMessageMode;
// 成功消息提示类型
successMessageMode?: SuccessMessageMode;
// 是否添加时间戳
joinTime?: boolean;
ignoreCancelToken?: boolean;
// 是否在标头中发送token
withToken?: boolean;
// 请求重试机制
retryRequest?: RetryRequest;
}
export interface RetryRequest {
isOpenRetry: boolean;
count: number;
waitTime: number;
}
export interface Result<T = any> {
code: number;
type: 'success' | 'error' | 'warning';
message: string;
result: T;
}
// multipart/form-data: upload file
// 文件上传
export interface UploadFileParams {
// 其他参数
data?: Recordable;
// 文件参数接口字段名称
name?: string;
// 文件名
file: File | Blob;
// 文件名
filename?: string;
[key: string]: any;
}
import { CreateAxiosOptions } from ‘./axios/axiosTransform’;
这样CreateAxiosOptions类型就ok了。
1.2、VAxios类
新建/@/utils/http/axios/index.ts文件作为axios模块类。
// /@/utils/http/axios/index.ts
import type {
AxiosRequestConfig,
InternalAxiosRequestConfig,
AxiosInstance,
AxiosResponse,
AxiosError,
} from 'axios';
import type {
RequestOptions, Result, UploadFileParams } from '/#/axios';
import type {
CreateAxiosOptions } from './axiosTransform';
import axios from 'axios';
import qs from 'qs';
import {
AxiosCanceler } from './axiosCancel';
import {
isFn } from '/@/utils/is';
import {
cloneDeep } from 'lodash-es';
import {
ContentTypeEnum } from '/@/enums/httpEnum';
import {
RequestEnum } from '/@/enums/httpEnum';
export * from './axiosTransform';
/**
* @description: axios模块
*/
export class VAxios {
private axiosInstance: AxiosInstance;
private readonly options: CreateAxiosOptions;
constructor(options: CreateAxiosOptions) {
this.options = options;
this.axiosInstance = axios.create(options);
this.setupInterceptors();
}
/**
* @description: 创建axios实例
*/
private createAxios(config: CreateAxiosOptions): void {
this.axiosInstance = axios.create(config);
}
private getTransform() {
const {
transform } = this.options;
return transform;
}
getAxios(): AxiosInstance {
return this.axiosInstance;
}
/**
* @description: 重新配置axios
*/
configAxios(config: CreateAxiosOptions) {
if (!this.axiosInstance) {
return;
}
this.createAxios(config);
}
/**
* @description: 设置通用header
*/
setHeader(headers: any): void {
if (!this.axiosInstance) {
return;
}
Object.assign(this.axiosInstance.defaults.headers, headers);
}
/**
* @description: 拦截器配置
*/
private setupInterceptors() {
const transform = this.getTransform();
if (!transform) {
return;
}
const {
requestInterceptors,
requestInterceptorsCatch,
responseInterceptors,
responseInterceptorsCatch,
} = transform;
const axiosCanceler = new AxiosCanceler();
// 请求拦截器配置处理
this.axiosInstance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
// 如果取消重复请求已打开,则禁止取消重复请求
// @ts-ignore
const {
ignoreCancelToken } = config.requestOptions;
const ignoreCancel =
ignoreCancelToken !== undefined
? ignoreCancelToken
: this.options.requestOptions?.ignoreCancelToken;
!ignoreCancel && axiosCanceler.addPending(config);
if (requestInterceptors && isFn(requestInterceptors)) {
config = requestInterceptors(config, this.options);
}
return config;
}, undefined);
// 请求拦截器错误捕获
requestInterceptorsCatch &&
isFn(requestInterceptorsCatch) &&
this.axiosInstance.interceptors.request.use(undefined, requestInterceptorsCatch);
// 响应结果拦截器处理
this.axiosInstance.interceptors.response.use((res: AxiosResponse<any>) => {
res && axiosCanceler.removePending(res.config);
if (responseInterceptors && isFn(responseInterceptors)) {
res = responseInterceptors(res);
}
return res;
}, undefined);
// 响应结果拦截器错误捕获
responseInterceptorsCatch &&
isFn(responseInterceptorsCatch) &&
this.axiosInstance.interceptors.response.use(undefined, (error