TypeScript利用TS封裝Axios實戰
簡介
這是TypeScript
實戰的第三篇文章。前面兩篇筆者分別介紹瞭在Vuex和Pinia中怎麼使用TypeScript
以及Vuex
和Pinia
的區別。今天我們再用TypeScript
封裝一遍Axios
。希望能進一步鞏固TypeScript
的基礎知識。
Axios幾個常用類型
在使用TypeScript
封裝Axios
之前我們先來看看Axios
幾個重要的類型。
AxiosRequestConfig
AxiosRequestConfig
是我們使用axios
發送請求傳遞參數的類型。當然它也是我們請求攔截器裡面的參數類型。
axios(config: AxiosRequestConfig)
可以看到,這個config
裡面的參數還是挺多的。我們常用的有url、method、params、data、headers、baseURL、timeout
。
export interface AxiosRequestConfig { url?: string; method?: Method; baseURL?: string; transformRequest?: AxiosTransformer | AxiosTransformer[]; transformResponse?: AxiosTransformer | AxiosTransformer[]; headers?: any; params?: any; paramsSerializer?: (params: any) => string; data?: any; timeout?: number; timeoutErrorMessage?: string; withCredentials?: boolean; adapter?: AxiosAdapter; auth?: AxiosBasicCredentials; responseType?: ResponseType; xsrfCookieName?: string; xsrfHeaderName?: string; onUploadProgress?: (progressEvent: any) => void; onDownloadProgress?: (progressEvent: any) => void; maxContentLength?: number; validateStatus?: ((status: number) => boolean) | null; maxBodyLength?: number; maxRedirects?: number; socketPath?: string | null; httpAgent?: any; httpsAgent?: any; proxy?: AxiosProxyConfig | false; cancelToken?: CancelToken; decompress?: boolean; transitional?: TransitionalOptions }
AxiosInstance
AxiosInstance
是我們使用axios
實例對象類型。
我們使用axios.create(config?: AxiosRequestConfig)
創建出來的對象都是AxiosInstance
類型
export interface AxiosInstance { (config: AxiosRequestConfig): AxiosPromise; (url: string, config?: AxiosRequestConfig): AxiosPromise; defaults: AxiosRequestConfig; interceptors: { request: AxiosInterceptorManager<AxiosRequestConfig>; response: AxiosInterceptorManager<AxiosResponse>; }; getUri(config?: AxiosRequestConfig): string; request<T = any, R = AxiosResponse<T>> (config: AxiosRequestConfig): Promise<R>; get<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>; delete<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>; head<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>; options<T = any, R = AxiosResponse<T>>(url: string, config?: AxiosRequestConfig): Promise<R>; post<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>; put<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>; patch<T = any, R = AxiosResponse<T>>(url: string, data?: any, config?: AxiosRequestConfig): Promise<R>; }
可以發現,我們可以使用axios.create、axios.all、axios.spread
方法,但是AxiosInstance
上並沒有create、all、spread
等方法,那我們的axios
到底是什麼類型呢?
AxiosStatic
export interface AxiosStatic extends AxiosInstance { create(config?: AxiosRequestConfig): AxiosInstance; Cancel: CancelStatic; CancelToken: CancelTokenStatic; isCancel(value: any): boolean; all<T>(values: (T | Promise<T>)[]): Promise<T[]>; spread<T, R>(callback: (...args: T[]) => R): (array: T[]) => R; isAxiosError(payload: any): payload is AxiosError; } declare const axios: AxiosStatic;
可以發現,axios
其實是AxiosStatic
類型,並且繼承瞭AxiosInstance
類型。所以是兩者的結合。相較axios.create(config?: AxiosRequestConfig)
創建出來的實例對象,axios
功能是更強大的。
AxiosResponse
AxiosResponse
是非常重要的,我們的axios
請求返回值類型都是AxiosResponse
類型。並且我們可以發現AxiosResponse
是一個接口泛型,這個泛型會應用到後端返回的data
上。所以這塊我們可以根據後端接口返回定義不同的類型傳遞進去。後面筆者在封裝常用方法的時候會細說。
export interface AxiosResponse<T = any> { data: T; status: number; statusText: string; headers: any; config: AxiosRequestConfig; request?: any; }
AxiosError
AxiosError
這個類型也是我們必須要知道的。在我們響應攔截器裡面的錯誤就是AxiosError
類型。
export interface AxiosError<T = any> extends Error { config: AxiosRequestConfig; code?: string; request?: any; response?: AxiosResponse<T>; isAxiosError: boolean; toJSON: () => object; }
說完瞭Axios
的幾個常用類型,接下來我們正式開始使用TS
來封裝我們的Axios
。
基礎封裝
首先我們實現一個最基本的版本,實例代碼如下:
// index.ts import axios from 'axios' import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' class Request { // axios 實例 instance: AxiosInstance // 基礎配置,url和超時時間 baseConfig: AxiosRequestConfig = {baseURL: "/api", timeout: 60000} constructor(config: AxiosRequestConfig) { // 使用axios.create創建axios實例 this.instance = axios.create(Object.assign(this.baseConfig, config)) } // 定義請求方法 public request(config: AxiosRequestConfig): Promise<AxiosResponse> { return this.instance.request(config) } } export default Request
在實際項目中有瞭基本的請求方法還是遠遠不夠的,我們還需要封裝攔截器和一些常用方法。
攔截器封裝
攔截器封裝隻需要在類中對axios.create()
創建的實例調用interceptors
下的兩個攔截器即可,
實例代碼如下:
// index.ts constructor(config: AxiosRequestConfig) { this.instance = axios.create(Object.assign(this.baseConfig, config)) this.instance.interceptors.request.use( (config: AxiosRequestConfig) => { // 一般會請求攔截裡面加token const token = localStorage.getItem("token") config.headers["Authorization"] = token; return config }, (err: any) => { return Promise.reject(err) }, ) this.instance.interceptors.response.use( (res: AxiosResponse) => { // 直接返回res,當然你也可以隻返回res.data return res }, (err: any) => { // 這裡用來處理http常見錯誤,進行全局提示 let message = ""; switch (err.response.status) { case 400: message = "請求錯誤(400)"; break; case 401: message = "未授權,請重新登錄(401)"; // 這裡可以做清空storage並跳轉到登錄頁的操作 break; case 403: message = "拒絕訪問(403)"; break; case 404: message = "請求出錯(404)"; break; case 408: message = "請求超時(408)"; break; case 500: message = "服務器錯誤(500)"; break; case 501: message = "服務未實現(501)"; break; case 502: message = "網絡錯誤(502)"; break; case 503: message = "服務不可用(503)"; break; case 504: message = "網絡超時(504)"; break; case 505: message = "HTTP版本不受支持(505)"; break; default: message = `連接出錯(${err.response.status})!`; } // 這裡錯誤消息可以使用全局彈框展示出來 // 比如element plus 可以使用 ElMessage ElMessage({ showClose: true, message: `${message},請檢查網絡或聯系管理員!`, type: "error", }); // 這裡是AxiosError類型,所以一般我們隻reject我們需要的響應即可 return Promise.reject(err.response) }, ) }
在這裡我們分別對請求攔截器和響應攔截器做瞭處理。在請求攔截器我們給請求頭添加瞭token
。
在響應攔截器,我們返回瞭整個response
對象,當然你也可以隻返回後端返回的response.data
,這裡可以根據個人喜好來處理。其次對http
錯誤進行瞭全局處理。
常用方法封裝
在基礎封裝的時候我們封裝瞭一個request
通用方法,其實我們還可以更具體的封裝get、post、put、delete
方法,讓我們使用更方便。
並且,我們前面分析到,AxiosResponse
其實是一個泛型接口,他可以接受一個泛型並應用到我們的data
上。所以我們可以在這裡再定義一個後端通用返回的數據類型。
比如假設我們某個項目後端接口不管請求成功與失敗,返回的結構永遠是code、message、results
的話我們可以定義一個這樣的數據類型。
type Result<T> = { code: number, message: string, result: T }
然後傳遞個各個方法:
public get<T = any>( url: string, config?: AxiosRequestConfig ): Promise<AxiosResponse<Result<T>>> { return this.instance.get(url, config); } public post<T = any>( url: string, data?: any, config?: AxiosRequestConfig ): Promise<AxiosResponse<Result<T>>> { return this.instance.post(url, data, config); } public put<T = any>( url: string, data?: any, config?: AxiosRequestConfig ): Promise<AxiosResponse<Result<T>>> { return this.instance.put(url, data, config); } public delete<T = any>( url: string, config?: AxiosRequestConfig ): Promise<AxiosResponse<Result<T>>> { return this.instance.delete(url, config); }
這樣當我們調用接口的時候就可以看到我們返回的data
的類型啦。就是我們定義的Result
類型。
所以我們可以直接得到自動提示:
上面調用接口的時候並沒有傳遞接口數據類型,所以我們的result
是any
類型,要想要每個接口都有類型提示,我們還需要給方法傳遞泛型。
我們再改進下,我們再定義一個login
接口返回值類型loginType
type loginType = { token: string; };
然後再調用方法的地方傳遞進去,然後我們再看看返回值data
的類型。
可以看到他是Result<loginType>
類型,這個loginType
就是result
的類型。
所以我們的result
還可以進一步的得到提示
當然每個接口都定義返回值類型固然好,但是會大大加大前端的工作量。我們在寫請求方法的時候也可以不傳遞接口返回值類型,這樣result
的類型就是any
。這個可以根據自身項目需求來選擇使用。
看到這小夥伴們是不是都弄懂瞭呢?如還有疑問歡迎留言。
總結
說瞭這麼多,有些小夥伴們可能有點暈瞭,下面筆者總結下整個axios
的封裝。
// index.ts import axios from "axios"; import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; type Result<T> = { code: number; message: string; result: T; }; class Request { // axios 實例 instance: AxiosInstance; // 基礎配置,url和超時時間 baseConfig: AxiosRequestConfig = { baseURL: "/api", timeout: 60000 }; constructor(config: AxiosRequestConfig) { // 使用axios.create創建axios實例 this.instance = axios.create(Object.assign(this.baseConfig, config)); this.instance.interceptors.request.use( (config: AxiosRequestConfig) => { // 一般會請求攔截裡面加token const token = localStorage.getItem("token"); config.headers["Authorization"] = token; return config; }, (err: any) => { return Promise.reject(err); } ); this.instance.interceptors.response.use( (res: AxiosResponse) => { // 直接返回res,當然你也可以隻返回res.data return res; }, (err: any) => { // 這裡用來處理http常見錯誤,進行全局提示 let message = ""; switch (err.response.status) { case 400: message = "請求錯誤(400)"; break; case 401: message = "未授權,請重新登錄(401)"; // 這裡可以做清空storage並跳轉到登錄頁的操作 break; case 403: message = "拒絕訪問(403)"; break; case 404: message = "請求出錯(404)"; break; case 408: message = "請求超時(408)"; break; case 500: message = "服務器錯誤(500)"; break; case 501: message = "服務未實現(501)"; break; case 502: message = "網絡錯誤(502)"; break; case 503: message = "服務不可用(503)"; break; case 504: message = "網絡超時(504)"; break; case 505: message = "HTTP版本不受支持(505)"; break; default: message = `連接出錯(${err.response.status})!`; } // 這裡錯誤消息可以使用全局彈框展示出來 // 比如element plus 可以使用 ElMessage ElMessage({ showClose: true, message: `${message},請檢查網絡或聯系管理員!`, type: "error", }); // 這裡是AxiosError類型,所以一般我們隻reject我們需要的響應即可 return Promise.reject(err.response); } ); } // 定義請求方法 public request(config: AxiosRequestConfig): Promise<AxiosResponse> { return this.instance.request(config); } public get<T = any>( url: string, config?: AxiosRequestConfig ): Promise<AxiosResponse<Result<T>>> { return this.instance.get(url, config); } public post<T = any>( url: string, data?: any, config?: AxiosRequestConfig ): Promise<AxiosResponse<Result<T>>> { return this.instance.post(url, data, config); } public put<T = any>( url: string, data?: any, config?: AxiosRequestConfig ): Promise<AxiosResponse<Result<T>>> { return this.instance.put(url, data, config); } public delete<T = any>( url: string, config?: AxiosRequestConfig ): Promise<AxiosResponse<Result<T>>> { return this.instance.delete(url, config); } } export default Request;
到此這篇關於TypeScript利用TS封裝Axios實戰的文章就介紹到這瞭,更多相關TypeScript封裝Axios內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 如何使用TS對axios的進行簡單封裝
- 十分鐘封裝一個好用的axios步驟示例
- 項目中使用Typescript封裝axios
- vue中Axios添加攔截器刷新token的實現方法
- 一篇文章讓你看懂封裝Axios