前景提要:
ts 簡易封裝 axios,統(tǒng)一 API
背景
axios 很多額外功能都是基于攔截器實現(xiàn)。有些功能想要全局使用,因此將攔截器注冊在全局。比如重復請求過濾。但也有一小部分請求不希望進行過濾,比如并發(fā)上傳文件。
因此希望可以在具體的請求方法上,通過配置 config 從而決定針對當前請求,某個攔截器是否啟用。
- 默認不寫配置項或者標為 true,都是開啟。
- 明確指定配置項為 false,才是關閉。
注意:在具體方法上配置時,沒有明確配置為 true 時,可能沒有開啟功能。因為在實例化時的 config 對象上,可能將該功能設為 false 了。整個 axios 實例上關閉了該功能,則此時具體方法上必須明確設為 true 覆蓋掉實例化的配置才能啟用。
以當前請求是否開啟響應反饋為例:
// index.ts
import HttpRequest from "./http/request";
// 實例化 axios
const httpRequest = new HttpRequest({
baseURL: import.meta.env.VITE_APP_API_BASE_URL,
timeout: import.meta.env.VITE_APP_API_TIMEOUT,
// 開啟響應反饋攔截器功能(注冊攔截器后,默認就是開啟)
showMessage: true
});
// 在該 axios 實例上注冊請求響應反饋攔截器
httpRequest.getInstance().interceptors.response.use(responseMessageOnFulfilled, null);
// 默認開啟當前請求的請求響應彈窗反饋
async function handleMessageOpen() {
const res = await httpRequest.get({
url: "/mock/test"
});
console.log("res", res);
}
// 手動關閉當前請求的響應反饋
async function handleMessageClose() {
const res = await httpRequest.get({
url: "/mock/test",
showMessage: false
});
console.log("res", res);
}
擴展 config 類型
給 axios config 對象添加額外配置項,首先得擴充它的類型AxiosRequestConfig:
// 開關的默認值(默認都是開啟)
const DEFAULT_EXTRA_FEATURE_CONFIG = { showMessage: true };
/** 擴展 axios 的請求配置類型 */
export interface MyAxiosRequestConfig<TReqBodyData = any> extends AxiosRequestConfig<TReqBodyData> {
showMessage?: boolean; // 是否開啟請求反饋提示框
}
攔截器中也需要用到 config 對象。但是在最新的 axios 中,請求攔截器中 config 的類型不再是 AxiosRequestConfig,而是InternalAxiosRequestConfig。因此還需要擴展一下請求攔截器 configInternalAxiosRequestConfig類型。
/** 給攔截器使用 */
export interface MyInternalAxiosRequestConfig extends InternalAxiosRequestConfig {
showMessage?: boolean;
}
有些功能在響應攔截器 onFulfilled 回調(diào)中完成,如響應反饋,也有些在 onRejected 回調(diào)中完成,如請求重試。因此也需要擴展響應攔截器中的回調(diào),AxiosResponse和AxiosError里的 config 對象類型。
export interface MyAxiosResponse extends AxiosResponse {
config: MyInternalAxiosRequestConfig;
}
export interface MyAxiosError extends AxiosError {
config: MyInternalAxiosRequestConfig;
}
將額外配置項注入響應攔截器
實現(xiàn)各種功能的攔截器,可能是請求攔截器,也可能是響應攔截器。請求攔截器中可以很輕易拿到具體方法 config 上填寫的配置項。但是響應攔截器卻不好拿,因為 AxiosResponse 實例上的 config 對象是新生成的,它沒有我們額外補充的配置項。
因此,我們需要在請求攔截器中先手動保存用戶填寫的配置項的值,然后在響應攔截器中手動添加到 config 對象上。并且該響應攔截器必須是最先執(zhí)行的響應攔截器。
class HttpRequest {
private readonly instance: AxiosInstance;
private readonly extraConfig: Record<string, boolean> = DEFAULT_EXTRA_FEATURE_CONFIG;
constructor(config: MyAxiosRequestConfig) {
this.instance = axios.create(config);
// 記錄 config 額外配置項
this.instance.interceptors.request.use((config: MyInternalAxiosRequestConfig) => {
Object.keys(this.extraConfig).forEach(item => {
if (config[item as keyof MyInternalAxiosRequestConfig] === false)
this.extraConfig[item] = !!config[item as keyof MyInternalAxiosRequestConfig];
});
return config;
});
// 將配置項補充到響應的 config 對象上
this.instance.interceptors.response.use(
(res: any) => {
Object.keys(this.extraConfig).forEach(item => {
res.config[item] = this.extraConfig[item];
this.extraConfig[item] = true; // 配置復原成默認開啟
});
return res;
},
(err: any) => {
Object.keys(this.extraConfig).forEach(item => {
err.config[item] = this.extraConfig[item];
this.extraConfig[item] = true; // 配置復原成默認開啟
});
throw err;
}
);
}
...
}
請求響應反饋攔截器
// src\api\http\message.ts
import { MyAxiosResponse } from "./request";
// 假設接口:{code: number, data: any, msg: string}
function showMessage(res: MyAxiosResponse) {
const { data } = res;
// alert 消息提示
if (data.code >= 200 && data.code < 300) alert(data.msg);
return res;
}
/**
* 響應業(yè)務消息提示
*/
export function responseMessageOnFulfilled(res: MyAxiosResponse) {
if (res.config.showMessage) showMessage(res); // showMessage 為 true,才給出反饋
return res;
}
使用示例
<template>
<div>
<button @click="handleMessageOpen">message消息提示開</button>
<button @click="handleMessageClose">message消息提示關</button>
</div>
</template>
<script setup lang="ts">
import HttpRequest from "./http/request";
import { responseMessageOnFulfilled } from "./http/message";
// 實例化
const httpRequest = new HttpRequest({
baseURL: "/",
timeout: 10000
});
// 注冊攔截器
httpRequest.getInstance().interceptors.response.use(responseMessageOnFulfilled);
// 反饋默認開
async function handleMessageOpen() {
const res = await httpRequest.get({
url: "/__api/mock/get_test"
// showMessage: true
});
console.log("res", res);
}
// 手動關閉反饋
async function handleMessageClose() {
const res = await httpRequest.get({
url: "/__api/mock/get_test",
showMessage: false
});
console.log("res", res);
}
</script>
結果:

總結
通過在 config 對象上添加額外配置的方式來實現(xiàn)針對某個請求開關某個攔截器的目標。
- 擴展 config 類型
- 將額外配置項從請求 config 保存到 響應 config 對象上
- 編寫具體功能的攔截器