Axios 二次封裝高級教程(含請求取消等功能)
整理不易,收藏、點贊、關注哦!
一、總體架構設計
-
目的:構建一套功能完善、易用且健壯的 HTTP 請求封裝層
-
核心功能:
- 請求攔截、響應攔截
- 請求取消(防止重復請求、接口防抖)
- 請求合并與批量處理
- 緩存策略(內存緩存、localStorage、sessionStorage)
- 失敗重試機制
- 上傳下載進度監聽
- 狀態化 hooks 集成(loading、error、data 管理)
- 統一接口調用管理
二、Axios 實例創建與基礎配置
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, Canceler } from 'axios'const pendingRequestMap = new Map<string, Canceler>()// 生成請求唯一key,便于取消重復請求
function getRequestKey(config: AxiosRequestConfig): string {const { method, url, params, data } = configreturn [method, url, JSON.stringify(params), JSON.stringify(data)].join('&')
}// 添加請求到pending隊列
function addPendingRequest(config: AxiosRequestConfig) {const requestKey = getRequestKey(config)if (pendingRequestMap.has(requestKey)) {// 取消重復請求pendingRequestMap.get(requestKey)?.('取消重復請求')}config.cancelToken = new axios.CancelToken((cancel) => {pendingRequestMap.set(requestKey, cancel)})
}// 移除請求
function removePendingRequest(config: AxiosRequestConfig) {const requestKey = getRequestKey(config)if (pendingRequestMap.has(requestKey)) {pendingRequestMap.delete(requestKey)}
}const service: AxiosInstance = axios.create({baseURL: import.meta.env.VITE_API_BASE_URL,timeout: 15000,headers: {'Content-Type': 'application/json;charset=UTF-8',},
})// 請求攔截器
service.interceptors.request.use((config) => {removePendingRequest(config) // 移除之前的請求addPendingRequest(config) // 添加當前請求// 這里可以加token等鑒權信息// const token = localStorage.getItem('token')// if (token) config.headers.Authorization = `Bearer ${token}`return config},(error) => Promise.reject(error)
)// 響應攔截器
service.interceptors.response.use((response: AxiosResponse) => {removePendingRequest(response.config)// 統一處理狀態碼const { data } = responseif (data.code !== 0) {// 自定義錯誤處理return Promise.reject(new Error(data.message || 'Error'))}return data},(error) => {if (axios.isCancel(error)) {console.warn('請求被取消:', error.message)return Promise.reject({ canceled: true })}return Promise.reject(error)}
)export default service
三、請求取消與防抖(防重復請求)
- 場景:用戶快速重復點擊按鈕,多次發送相同請求
- 解決:通過
cancelToken
取消前一個重復請求,保證接口調用穩定
核心邏輯已在上面pendingRequestMap
實現,結合唯一請求Key判斷。
四、多請求合并(批量接口請求)
/*** 批量請求示例* @param urls 請求地址數組*/
function batchRequest(urls: string[]) {const requests = urls.map((url) => service.get(url))return axios.all(requests).then(axios.spread((...responses) => responses))
}// 使用
batchRequest(['/api/a', '/api/b', '/api/c']).then(([resA, resB, resC]) => {console.log(resA, resB, resC)
})
五、緩存策略(內存緩存 + 本地緩存)
const cacheMap = new Map<string, any>()// 簡單緩存示例
function requestWithCache<T = any>(config: AxiosRequestConfig, useCache = true): Promise<T> {const key = getRequestKey(config)if (useCache && cacheMap.has(key)) {return Promise.resolve(cacheMap.get(key))}return service.request<T>(config).then((res) => {cacheMap.set(key, res)return res})
}
- 可根據業務需求,擴展使用
localStorage
或sessionStorage
緩存,并設置過期時間。
六、自動失敗重試機制
interface RetryConfig extends AxiosRequestConfig {retry?: number // 重試次數retryDelay?: number // 重試間隔ms
}function retryRequest(config: RetryConfig): Promise<any> {const { retry = 3, retryDelay = 1000 } = configlet currentRetry = 0const request = (): Promise<any> => {return service.request(config).catch((error) => {if (currentRetry < retry) {currentRetry++return new Promise((resolve) => setTimeout(resolve, retryDelay)).then(request)}return Promise.reject(error)})}return request()
}
- 可以在請求層或調用層根據需求靈活調用。
七、上傳和下載進度監聽
// 上傳進度
function uploadFile(url: string, file: File, onProgress: (percent: number) => void) {const formData = new FormData()formData.append('file', file)return service.post(url, formData, {headers: { 'Content-Type': 'multipart/form-data' },onUploadProgress: (progressEvent) => {const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total)onProgress(percent)},})
}// 下載進度
function downloadFile(url: string, onProgress: (percent: number) => void) {return service.get(url, {responseType: 'blob',onDownloadProgress: (progressEvent) => {const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total)onProgress(percent)},})
}
八、封裝狀態式 Hook(結合 Vue 3 Composition API)
import { ref } from 'vue'interface UseRequestOptions {manual?: boolean // 是否手動調用retry?: numberretryDelay?: number
}export function useRequest<T = any>(requestFn: (...args: any[]) => Promise<T>,options: UseRequestOptions = {}
) {const data = ref<T | null>(null)const loading = ref(false)const error = ref<Error | null>(null)const fetch = async (...args: any[]) => {loading.value = trueerror.value = nulltry {data.value = await requestFn(...args)loading.value = falsereturn data.value} catch (err: any) {error.value = errloading.value = falsethrow err}}if (!options.manual) {fetch()}return { data, loading, error, fetch }
}
- 使用示例:
import { useRequest } from './useRequest'
import service from './axios'const { data, loading, error, fetch } = useRequest(() => service.get('/api/user'))
九、文件上傳并發送給后端(結合FileReader)
function uploadFileWithPreview(file: File, url: string) {return new Promise((resolve, reject) => {const reader = new FileReader()reader.readAsDataURL(file) // 讀取文件為base64編碼reader.onload = () => {// 可以先預覽file的base64,也可以直接上傳二進制const formData = new FormData()formData.append('file', file) // 直接上傳文件service.post(url, formData, {headers: { 'Content-Type': 'multipart/form-data' },}).then(resolve).catch(reject)}reader.onerror = (err) => reject(err)})
}
十、總結
- 請求取消通過唯一請求key + CancelToken實現,防止重復請求。
- 多請求合并使用
axios.all
和axios.spread
實現批量請求并行。 - 緩存策略基于請求key設計內存或本地緩存,提升請求效率。
- 自動重試可根據失敗情況自動重試,增強請求魯棒性。
- 上傳/下載進度監聽靈活支持文件上傳和下載的進度反饋。
- 狀態式 Hook結合 Vue 3 Composition API,實現簡潔易用的請求狀態管理。
- 文件上傳可結合 FileReader 實現預覽和上傳雙功能。