ts封裝axios最佳實踐示例詳解
簡介
??看了一圈,大家對 ts
封裝 axios
都各有見解。但都不是我滿意的吧,所以自己封裝了一個??。至于為什么敢叫最佳實踐,因為我滿意,就這么簡單粗暴????。
什么樣封裝才是最合理的
別再用 promise
包了,好嗎?
看了一下,很多人封裝 axios
的時候都用 promise
包裝了一層,甚至更有甚者用起了 try catch
。為什么反對用 promise
包裝,因為 axios
返回的就是個 promise
,脫褲子放屁,完全沒必要??♀?。至于 try catch
這個是用于捕獲未知錯誤的,比如 JSON.parse
的時候,有些字符串就是無法轉(zhuǎn)換。記住一句話,濫用 try catch
和隨地大小便沒有區(qū)別。
一個 request
方法梭哈,噗!我一口老血??
部分人直接就一個 request
方法梭哈,所有參數(shù)與配置都寫在一起,看起來一點也不清晰,簡潔。請求有多種方式,get
,post
,put...
,最合理的請求方式應(yīng)該是 instance[method](url, data, options)
。對應(yīng) 請求地址、請求參數(shù)、請求配置項,一目了然。
擴(kuò)展我需要的請求,不要再 ts-ignore
了??
如果 ts-ignore
用多了,就會產(chǎn)生依賴性。不排除情況緊急急著上線,或者 類型處理 復(fù)雜的,但是在有時間的時候,還是得優(yōu)化一下,作為程序員,追求優(yōu)雅,永不過時。
求你了!把攔截器拿出來吧??
封裝的時候我們都會封裝一個請求類,但對應(yīng)攔截器應(yīng)該解耦出來。因為每個域名的攔截器處理可能不一致,寫死的話封裝請求類的意義也就沒有了。
接口請求 then
里面又判斷后端返回碼判斷請求是否成功,太狗血了!??
????看到下面這種代碼,給我難受的啊。
api.post(url, data).then((res) => { if (res.code === 1) { // ... } else { // 全局消息提示 console.error(res.message) } })
既然是一個 promise
,我們就應(yīng)該知道 promise
只有成功或者失敗。then
怎么會有成功錯誤的處理呢?then
里面就是請求成功,沒有什么 if else
,處理失敗去 catch
里面處理去。這么喜歡寫 if else
,你是沒寫過單元測試是吧?
開整
OK
,吐槽了這么多,這時候肯定就有人說了,光說誰不會啊,你整一個啊!??
瞧你這話說的,一點活沒干,還讓你白嫖了。你咋這么能呢???
不過話說回來,我不要活在他人的評價里,我做這件事情不是因為你的諷刺或者吹捧,而是我自己要做????。
接下來定一下要做的事情
- 封裝一個請求類
- 適當(dāng)擴(kuò)展
axios
類型,為自定義配置打地基 - 支持自定義請求配置。如是否全局錯誤提示
- 攔截器單獨拎出來,方便擴(kuò)展新的請求
開整之前先看看 axios 基本類型
// 這是 axios 請求類型定義,但是因為我們需要支持自定義配置,所以待會需要把它拓展一下 export interface AxiosRequestConfig<D = any> { url?: string; method?: Method | string; baseURL?: string; transformRequest?: AxiosRequestTransformer | AxiosRequestTransformer[]; transformResponse?: AxiosResponseTransformer | AxiosResponseTransformer[]; headers?: (RawAxiosRequestHeaders & MethodsHeaders) | AxiosHeaders; params?: any; paramsSerializer?: ParamsSerializerOptions; data?: D; timeout?: Milliseconds; timeoutErrorMessage?: string; withCredentials?: boolean; // ... } // 這是 axios 請求返回類型定義,里面類型需要處理,所以這個我們也得處理一下。 export interface AxiosResponse<T = any, D = any> { data: T; status: number; statusText: string; headers: RawAxiosResponseHeaders | AxiosResponseHeaders; // 這里的配置沒有支持拓展,所以待會也得處理一下 config: InternalAxiosRequestConfig<D>; request?: any; } // 所以我們只需要改造 3 個 axios 類型定義就行了 // 另外我們需要定義下自己的攔截器 和 請求結(jié)果封裝
Talk is cheap,show me the code.
代碼也不多,就也不多解釋了,基本注釋都加上了。下面是全部代碼。
import axios from 'axios' import type { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios' // 定義一個常見后端請求返回 type BaseApiResponse<T> = { code: number message: string result: T } // 拓展 axios 請求配置,加入我們自己的配置 interface RequestOptions { // 是否全局展示請求 錯誤信息 globalErrorMessage?: boolean // 是否全局展示請求 成功信息 globalSuccessMessage?: boolean } // 拓展自定義請求配置 interface ExpandAxiosRequestConfig<D = any> extends AxiosRequestConfig<D> { interceptorHooks?: InterceptorHooks requestOptions?: RequestOptions } // 拓展 axios 請求配置 interface ExpandInternalAxiosRequestConfig<D = any> extends InternalAxiosRequestConfig<D> { interceptorHooks?: InterceptorHooks requestOptions?: RequestOptions } // 拓展 axios 返回配置 interface ExpandAxiosResponse<T = any, D = any> extends AxiosResponse<T, D> { config: ExpandInternalAxiosRequestConfig<D> } export interface InterceptorHooks { requestInterceptor?: (config: InternalAxiosRequestConfig) => InternalAxiosRequestConfig requestInterceptorCatch?: (error: any) => any responseInterceptor?: (response: AxiosResponse) => AxiosResponse | Promise<AxiosResponse> responseInterceptorCatch?: (error: any) => any } // 導(dǎo)出Request類,可以用來自定義傳遞配置來創(chuàng)建實例 export default class Request { // axios 實例 private _instance: AxiosInstance // 默認(rèn)配置 private _defaultConfig: ExpandAxiosRequestConfig = { baseURL: '/api', timeout: 5000, requestOptions: { globalErrorMessage: true, globalSuccessMessage: false } } private _interceptorHooks?: InterceptorHooks constructor(config: ExpandAxiosRequestConfig) { // 使用axios.create創(chuàng)建axios實例 this._instance = axios.create(Object.assign(this._defaultConfig, config)) this._interceptorHooks = config.interceptorHooks this.setupInterceptors() } // 通用攔截,在初始化時就進(jìn)行注冊和運行,對基礎(chǔ)屬性進(jìn)行處理 private setupInterceptors() { this._instance.interceptors.request.use(this._interceptorHooks?.requestInterceptor, this._interceptorHooks?.requestInterceptorCatch) this._instance.interceptors.response.use(this._interceptorHooks?.responseInterceptor, this._interceptorHooks?.responseInterceptorCatch) } // 定義核心請求 public request(config: ExpandAxiosRequestConfig): Promise<AxiosResponse> { // ?。?!?? 注意:axios 已經(jīng)將請求使用 promise 封裝過了 // 這里直接返回,不需要我們再使用 promise 封裝一層 return this._instance.request(config) } public get<T = any>(url: string, config?: ExpandAxiosRequestConfig): Promise<AxiosResponse<BaseApiResponse<T>>> { return this._instance.get(url, config) } public post<T = any>(url: string, data?: any, config?: ExpandAxiosRequestConfig): Promise<T> { return this._instance.post(url, data, config) } public put<T = any>(url: string, data?: any, config?: ExpandAxiosRequestConfig): Promise<T> { return this._instance.put(url, data, config) } public delete<T = any>(url: string, config?: ExpandAxiosRequestConfig): Promise<T> { return this._instance.delete(url, config) } }
以及使用的 demo
。這個保姆級服務(wù)滿意嗎?
// 請求攔截器 const transform: InterceptorHooks = { requestInterceptor(config) { // 請求頭部處理,如添加 token const token = 'token-value' if (token) { config!.headers!.Authorization = token } return config }, requestInterceptorCatch(err) { // 請求錯誤,這里可以用全局提示框進(jìn)行提示 return Promise.reject(err) }, responseInterceptor(result) { // 因為 axios 返回不支持?jǐn)U展自定義配置,需要自己斷言一下 const res = result as ExpandAxiosResponse // 與后端約定的請求成功碼 const SUCCESS_CODE = 1 if (res.status !== 200) return Promise.reject(res) if (res.data.code !== SUCCESS_CODE) { if (res.config.requestOptions?.globalErrorMessage) { // 這里全局提示錯誤 console.error(res.data.message) } return Promise.reject(res.data) } if (res.config.requestOptions?.globalSuccessMessage) { // 這里全局提示請求成功 console.log(res.data.message) } // 請求返回值,建議將 返回值 進(jìn)行解構(gòu) return res.data.result }, responseInterceptorCatch(err) { // 這里用來處理 http 常見錯誤,進(jìn)行全局提示 const mapErrorStatus = new Map([ [400, '請求方式錯誤'], [401, '請重新登錄'], [403, '拒絕訪問'], [404, '請求地址有誤'], [500, '服務(wù)器出錯'] ]) const message = mapErrorStatus.get(err.response.status) || '請求出錯,請稍后再試' // 此處全局報錯 console.error(message) return Promise.reject(err.response) } } // 具體使用時先實例一個請求對象 const request = new Request({ baseURL: '/api', timeout: 5000, interceptorHooks: transform }) // 定義請求返回 interface ResModel { str: string num: number } // 發(fā)起請求 request .post<ResModel>( '/abc', { a: 'aa', b: 'bb' }, { requestOptions: { globalErrorMessage: true } } ) .then((res) => { console.log('res: ', res) console.log(res.str) })
可以看到鼠標(biāo)浮上去就能看到定義了,完美!
最后源碼地址: github.com/coveychen95…
以上就是ts封裝axios最佳實踐示例詳解的詳細(xì)內(nèi)容,更多關(guān)于ts封裝axios的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
iframe的onload在Chrome/Opera中執(zhí)行兩次Bug的解決方法
創(chuàng)建iframe對象,添加load事件, 再將iframe添加到body中。Chrome/Opera中會造成load事件的handler執(zhí)行兩次。2011-03-03一文詳解JavaScript中的URL和URLSearchParams
URL,稱為統(tǒng)一資源定位器,指互聯(lián)網(wǎng)上能找到資源定位的字符串,而URLSearchParams對象是專門用于處理url網(wǎng)址信息中的查詢字符串,本文就來帶大家深入了解一下二者的使用2023-05-05深入聊聊Array的sort方法的使用技巧.詳細(xì)點評protype.js中的sortBy方法
深入聊聊Array的sort方法的使用技巧.詳細(xì)點評protype.js中的sortBy方法...2007-04-04一個js隨機(jī)顏色腳本(用于標(biāo)簽頁面,也可用于任何頁面)
一個js隨機(jī)顏色腳本(用于標(biāo)簽頁面,也可用于任何頁面)...2007-09-09按鍵測試,支持像 Ctrl+Alt+Shift+T 的組合鍵
按鍵測試,支持像 Ctrl+Alt+Shift+T 的組合鍵...2006-10-10vscode錄音及語音實時轉(zhuǎn)寫插件開發(fā)并在工作區(qū)生成本地mp3文件附踩坑日記!
以目前的vscode版本來說,作者并沒有開放訪問本地媒體權(quán)限,所以插件市場里面的所有語音相關(guān)插件也并沒有直接獲取vscode的媒體權(quán)限,這篇文章主要介紹了vscode錄音及語音實時轉(zhuǎn)寫插件開發(fā)并在工作區(qū)生成本地mp3文件?踩坑日記!,需要的朋友可以參考下2023-05-05