TypeScript利用TS封裝Axios實(shí)戰(zhàn)
簡介
這是TypeScript
實(shí)戰(zhàn)的第三篇文章。前面兩篇筆者分別介紹了在Vuex和Pinia中怎么使用TypeScript
以及Vuex
和Pinia
的區(qū)別。今天我們?cè)儆?code>TypeScript封裝一遍Axios
。希望能進(jìn)一步鞏固TypeScript
的基礎(chǔ)知識(shí)。
Axios幾個(gè)常用類型
在使用TypeScript
封裝Axios
之前我們先來看看Axios
幾個(gè)重要的類型。
AxiosRequestConfig
AxiosRequestConfig
是我們使用axios
發(fā)送請(qǐng)求傳遞參數(shù)的類型。當(dāng)然它也是我們請(qǐng)求攔截器里面的參數(shù)類型。
axios(config: AxiosRequestConfig)
可以看到,這個(gè)config
里面的參數(shù)還是挺多的。我們常用的有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
實(shí)例對(duì)象類型。
我們使用axios.create(config?: AxiosRequestConfig)
創(chuàng)建出來的對(duì)象都是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>; }
可以發(fā)現(xiàn),我們可以使用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;
可以發(fā)現(xiàn),axios
其實(shí)是AxiosStatic
類型,并且繼承了AxiosInstance
類型。所以是兩者的結(jié)合。相較axios.create(config?: AxiosRequestConfig)
創(chuàng)建出來的實(shí)例對(duì)象,axios
功能是更強(qiáng)大的。
AxiosResponse
AxiosResponse
是非常重要的,我們的axios
請(qǐng)求返回值類型都是AxiosResponse
類型。并且我們可以發(fā)現(xiàn)AxiosResponse
是一個(gè)接口泛型,這個(gè)泛型會(huì)應(yīng)用到后端返回的data
上。所以這塊我們可以根據(jù)后端接口返回定義不同的類型傳遞進(jìn)去。后面筆者在封裝常用方法的時(shí)候會(huì)細(xì)說。
export interface AxiosResponse<T = any> { data: T; status: number; statusText: string; headers: any; config: AxiosRequestConfig; request?: any; }
AxiosError
AxiosError
這個(gè)類型也是我們必須要知道的。在我們響應(yīng)攔截器里面的錯(cuò)誤就是AxiosError
類型。
export interface AxiosError<T = any> extends Error { config: AxiosRequestConfig; code?: string; request?: any; response?: AxiosResponse<T>; isAxiosError: boolean; toJSON: () => object; }
說完了Axios
的幾個(gè)常用類型,接下來我們正式開始使用TS
來封裝我們的Axios
。
基礎(chǔ)封裝
首先我們實(shí)現(xiàn)一個(gè)最基本的版本,實(shí)例代碼如下:
// index.ts import axios from 'axios' import type { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' class Request { // axios 實(shí)例 instance: AxiosInstance // 基礎(chǔ)配置,url和超時(shí)時(shí)間 baseConfig: AxiosRequestConfig = {baseURL: "/api", timeout: 60000} constructor(config: AxiosRequestConfig) { // 使用axios.create創(chuàng)建axios實(shí)例 this.instance = axios.create(Object.assign(this.baseConfig, config)) } // 定義請(qǐng)求方法 public request(config: AxiosRequestConfig): Promise<AxiosResponse> { return this.instance.request(config) } } export default Request
在實(shí)際項(xiàng)目中有了基本的請(qǐng)求方法還是遠(yuǎn)遠(yuǎn)不夠的,我們還需要封裝攔截器和一些常用方法。
攔截器封裝
攔截器封裝只需要在類中對(duì)axios.create()
創(chuàng)建的實(shí)例調(diào)用interceptors
下的兩個(gè)攔截器即可,
實(shí)例代碼如下:
// index.ts constructor(config: AxiosRequestConfig) { this.instance = axios.create(Object.assign(this.baseConfig, config)) this.instance.interceptors.request.use( (config: AxiosRequestConfig) => { // 一般會(huì)請(qǐng)求攔截里面加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,當(dāng)然你也可以只返回res.data return res }, (err: any) => { // 這里用來處理http常見錯(cuò)誤,進(jìn)行全局提示 let message = ""; switch (err.response.status) { case 400: message = "請(qǐng)求錯(cuò)誤(400)"; break; case 401: message = "未授權(quán),請(qǐng)重新登錄(401)"; // 這里可以做清空storage并跳轉(zhuǎn)到登錄頁的操作 break; case 403: message = "拒絕訪問(403)"; break; case 404: message = "請(qǐng)求出錯(cuò)(404)"; break; case 408: message = "請(qǐng)求超時(shí)(408)"; break; case 500: message = "服務(wù)器錯(cuò)誤(500)"; break; case 501: message = "服務(wù)未實(shí)現(xiàn)(501)"; break; case 502: message = "網(wǎng)絡(luò)錯(cuò)誤(502)"; break; case 503: message = "服務(wù)不可用(503)"; break; case 504: message = "網(wǎng)絡(luò)超時(shí)(504)"; break; case 505: message = "HTTP版本不受支持(505)"; break; default: message = `連接出錯(cuò)(${err.response.status})!`; } // 這里錯(cuò)誤消息可以使用全局彈框展示出來 // 比如element plus 可以使用 ElMessage ElMessage({ showClose: true, message: `${message},請(qǐng)檢查網(wǎng)絡(luò)或聯(lián)系管理員!`, type: "error", }); // 這里是AxiosError類型,所以一般我們只reject我們需要的響應(yīng)即可 return Promise.reject(err.response) }, ) }
在這里我們分別對(duì)請(qǐng)求攔截器和響應(yīng)攔截器做了處理。在請(qǐng)求攔截器我們給請(qǐng)求頭添加了token
。
在響應(yīng)攔截器,我們返回了整個(gè)response
對(duì)象,當(dāng)然你也可以只返回后端返回的response.data
,這里可以根據(jù)個(gè)人喜好來處理。其次對(duì)http
錯(cuò)誤進(jìn)行了全局處理。
常用方法封裝
在基礎(chǔ)封裝的時(shí)候我們封裝了一個(gè)request
通用方法,其實(shí)我們還可以更具體的封裝get、post、put、delete
方法,讓我們使用更方便。
并且,我們前面分析到,AxiosResponse
其實(shí)是一個(gè)泛型接口,他可以接受一個(gè)泛型并應(yīng)用到我們的data
上。所以我們可以在這里再定義一個(gè)后端通用返回的數(shù)據(jù)類型。
比如假設(shè)我們某個(gè)項(xiàng)目后端接口不管請(qǐng)求成功與失敗,返回的結(jié)構(gòu)永遠(yuǎn)是code、message、results
的話我們可以定義一個(gè)這樣的數(shù)據(jù)類型。
type Result<T> = { code: number, message: string, result: T }
然后傳遞個(gè)各個(gè)方法:
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); }
這樣當(dāng)我們調(diào)用接口的時(shí)候就可以看到我們返回的data
的類型啦。就是我們定義的Result
類型。
所以我們可以直接得到自動(dòng)提示:
上面調(diào)用接口的時(shí)候并沒有傳遞接口數(shù)據(jù)類型,所以我們的result
是any
類型,要想要每個(gè)接口都有類型提示,我們還需要給方法傳遞泛型。
我們?cè)俑倪M(jìn)下,我們?cè)俣x一個(gè)login
接口返回值類型loginType
type loginType = { token: string; };
然后再調(diào)用方法的地方傳遞進(jìn)去,然后我們?cè)倏纯捶祷刂?code>data的類型。
可以看到他是Result<loginType>
類型,這個(gè)loginType
就是result
的類型。
所以我們的result
還可以進(jìn)一步的得到提示
當(dāng)然每個(gè)接口都定義返回值類型固然好,但是會(huì)大大加大前端的工作量。我們?cè)趯懻?qǐng)求方法的時(shí)候也可以不傳遞接口返回值類型,這樣result
的類型就是any
。這個(gè)可以根據(jù)自身項(xiàng)目需求來選擇使用。
看到這小伙伴們是不是都弄懂了呢?如還有疑問歡迎留言。
總結(jié)
說了這么多,有些小伙伴們可能有點(diǎn)暈了,下面筆者總結(jié)下整個(gè)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 實(shí)例 instance: AxiosInstance; // 基礎(chǔ)配置,url和超時(shí)時(shí)間 baseConfig: AxiosRequestConfig = { baseURL: "/api", timeout: 60000 }; constructor(config: AxiosRequestConfig) { // 使用axios.create創(chuàng)建axios實(shí)例 this.instance = axios.create(Object.assign(this.baseConfig, config)); this.instance.interceptors.request.use( (config: AxiosRequestConfig) => { // 一般會(huì)請(qǐng)求攔截里面加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,當(dāng)然你也可以只返回res.data return res; }, (err: any) => { // 這里用來處理http常見錯(cuò)誤,進(jìn)行全局提示 let message = ""; switch (err.response.status) { case 400: message = "請(qǐng)求錯(cuò)誤(400)"; break; case 401: message = "未授權(quán),請(qǐng)重新登錄(401)"; // 這里可以做清空storage并跳轉(zhuǎn)到登錄頁的操作 break; case 403: message = "拒絕訪問(403)"; break; case 404: message = "請(qǐng)求出錯(cuò)(404)"; break; case 408: message = "請(qǐng)求超時(shí)(408)"; break; case 500: message = "服務(wù)器錯(cuò)誤(500)"; break; case 501: message = "服務(wù)未實(shí)現(xiàn)(501)"; break; case 502: message = "網(wǎng)絡(luò)錯(cuò)誤(502)"; break; case 503: message = "服務(wù)不可用(503)"; break; case 504: message = "網(wǎng)絡(luò)超時(shí)(504)"; break; case 505: message = "HTTP版本不受支持(505)"; break; default: message = `連接出錯(cuò)(${err.response.status})!`; } // 這里錯(cuò)誤消息可以使用全局彈框展示出來 // 比如element plus 可以使用 ElMessage ElMessage({ showClose: true, message: `${message},請(qǐng)檢查網(wǎng)絡(luò)或聯(lián)系管理員!`, type: "error", }); // 這里是AxiosError類型,所以一般我們只reject我們需要的響應(yīng)即可 return Promise.reject(err.response); } ); } // 定義請(qǐng)求方法 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;
到此這篇關(guān)于TypeScript利用TS封裝Axios實(shí)戰(zhàn)的文章就介紹到這了,更多相關(guān)TypeScript封裝Axios內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript高級(jí)程序設(shè)計(jì) 閱讀筆記(十九) js表格排序
js表格排序?qū)崿F(xiàn)代碼,需要的朋友可以參考下2012-08-08基于原生js實(shí)現(xiàn)判斷元素是否有指定class名
這篇文章主要介紹了基于原生js實(shí)現(xiàn)判斷元素是否有指定class名,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07