axios 攔截器管理類鏈式調用手寫實現及原理剖析
axios庫的攔截器使用
我們知道axios
庫的攔截器的使用方式如下:
// 添加一個請求攔截器 axios.interceptors.request.use(function (config) { // 在發送請求之前可以做一些事情 return config; }, function (error) { // 處理請求錯誤 return Promise.reject(error); }); // 添加一個響應攔截器 axios.interceptors.response.use(function (response) { // 處理響應數據 return response; }, function (error) { // 處理響應錯誤 return Promise.reject(error); });
在 axios
對象上有一個 interceptors
對象屬性,該屬性又有 request
和 response
2 個屬性,它們都有一個 use
方法,use
方法支持 2 個參數,第一個參數類似 Promise.then
的 resolve
函數,第二個參數類似 Promise.then
的 reject
函數。我們可以在 resolve
函數和 reject
函數中執行同步代碼或者是異步代碼邏輯。
並且我們是可以添加多個攔截器的,攔截器的執行順序是鏈式依次執行的方式。對於 request
攔截器,後添加的攔截器會在請求前的過程中先執行;對於 response
攔截器,先添加的攔截器會在響應後先執行。
axios.interceptors.request.use(config => { config.headers.test += '1' return config }) axios.interceptors.request.use(config => { config.headers.test += '2' return config })
此外,我們也可以支持刪除某個攔截器,如下:
const myInterceptor = axios.interceptors.request.use(function () {/*...*/}) axios.interceptors.request.eject(myInterceptor)
整體設計
我們先用一張圖來展示一下攔截器工作流程:
整個過程是一個鏈式調用的方式,並且每個攔截器都可以支持同步和異步處理,我們自然而然地就聯想到使用 Promise 鏈的方式來實現整個調用過程。
在這個 Promise 鏈的執行過程中,請求攔截器 resolve
函數處理的是 config
對象,而相應攔截器 resolve
函數處理的是 response
對象。
在瞭解瞭攔截器工作流程後,我們先要創建一個攔截器管理類,允許我們去添加 刪除和遍歷攔截器。
攔截器管理類實現
根據需求,axios
擁有一個 interceptors
對象屬性,該屬性又有 request
和 response
2 個屬性,它們對外提供一個 use
方法來添加攔截器,我們可以把這倆屬性看做是一個攔截器管理對象。
use
方法支持 2 個參數,第一個是 resolve
函數,第二個是 reject
函數,對於 resolve
函數的參數,請求攔截器是 AxiosRequestConfig
類型的,而響應攔截器是 AxiosResponse
類型的;而對於 reject
函數的參數類型則是 any
類型的。
根據上述分析,我們先來定義一下攔截器管理對象對外的接口。
接口定義
這裡我們定義瞭 AxiosInterceptorManager
泛型接口,因為對於 resolve
函數的參數,請求攔截器和響應攔截器是不同的。
export interface AxiosInterceptorManager<T> { use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number eject(id: number): void } export interface ResolvedFn<T=any> { (val: T): T | Promise<T> } export interface RejectedFn { (error: any): any }
代碼實現
import { ResolvedFn, RejectedFn } from '../types' interface Interceptor<T> { resolved: ResolvedFn<T> rejected?: RejectedFn } export default class InterceptorManager<T> { private interceptors: Array<Interceptor<T> | null> constructor() { // 攔截器數組 this.interceptors = [] } // 收集攔截器 use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number { this.interceptors.push({ resolved, rejected }) return this.interceptors.length - 1 } // 遍歷用戶寫的攔截器,並執行fn函數把攔截器作為參數傳入 forEach(fn: (interceptor: Interceptor<T>) => void): void { this.interceptors.forEach(interceptor => { if (interceptor !== null) { fn(interceptor) } }) } eject(id: number): void { if (this.interceptors[id]) { // 置為null,不能直接刪除 this.interceptors[id] = null } } }
我們定義瞭一個 InterceptorManager
泛型類,內部維護瞭一個私有屬性 interceptors
,它是一個數組,用來存儲攔截器。該類還對外提供瞭 3 個方法,其中 use
接口就是添加攔截器到 interceptors
中,並返回一個 id
用於刪除;
forEach
接口就是遍歷 interceptors
用的,它支持傳入一個函數,遍歷過程中會調用該函數,並把每一個 interceptor
作為該函數的參數傳入;eject
就是刪除一個攔截器,通過傳入攔截器的 id
刪除。
鏈式調用實現
當我們實現好攔截器管理類,接下來就是在 Axios
中定義一個 interceptors
屬性,它的類型如下:
interface Interceptors { request: InterceptorManager<AxiosRequestConfig> response: InterceptorManager<AxiosResponse> } export default class Axios { interceptors: Interceptors constructor() { this.interceptors = { request: new InterceptorManager<AxiosRequestConfig>(), response: new InterceptorManager<AxiosResponse>() } } }
Interceptors
類型擁有 2 個屬性,一個請求攔截器管理類實例,一個是響應攔截器管理類實例。我們在實例化 Axios
類的時候,在它的構造器去初始化這個 interceptors
實例屬性。
接下來,我們修改 request
方法的邏輯,添加攔截器鏈式調用的邏輯:
interface PromiseChain { resolved: ResolvedFn | ((config: AxiosRequestConfig) => AxiosPromise) rejected?: RejectedFn } request(url: any, config?: any): AxiosPromise { if (typeof url === 'string') { if (!config) { config = {} } config.url = url } else { config = url } // 定義一個數組,這個數組就是要執行的任務鏈,默認有一個真正發送請求的任務 const chain: PromiseChain[] = [{ resolved: dispatchRequest, rejected: undefined }] // 把用戶定義的請求攔截器存放到任務鏈中,請求攔截器最後註冊的最先執行,所以使用unshift方法 this.interceptors.request.forEach(interceptor => { chain.unshift(interceptor) }) // 把響應攔截器存放到任務鏈中 this.interceptors.response.forEach(interceptor => { chain.push(interceptor) }) // 利用config初始化一個promise let promise = Promise.resolve(config) // 遍歷任務鏈 while (chain.length) { // 取出任務鏈的首個任務 const { resolved, rejected } = chain.shift()! // resolved的執行時機是就是上一個promise執行resolve()的時候,這樣就形成瞭鏈式調用 promise = promise.then(resolved, rejected) } return promise }
首先,構造一個 PromiseChain
類型的數組 chain
,並把 dispatchRequest
函數賦值給 resolved
屬性;接著先遍歷請求攔截器插入到 chain
的前面;然後再遍歷響應攔截器插入到 chain
後面。
接下來定義一個已經 resolve 的 promise
,循環這個 chain
,拿到每個攔截器對象,把它們的 resolved
函數和 rejected
函數添加到 promise.then
的參數中,這樣就相當於通過 Promise 的鏈式調用方式,實現瞭攔截器一層層的鏈式調用的效果。
註意我們攔截器的執行順序,對於請求攔截器,先執行後添加的,再執行先添加的;而對於響應攔截器,先執行先添加的,後執行後添加的。
以上就是axios 攔截器管理類鏈式調用手寫實現及原理剖析的詳細內容,更多關於axios 攔截器管理類鏈式調用的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- 一文掌握ajax、fetch和axios的區別對比
- 解決axios:"timeout of 5000ms exceeded"超時的問題
- axios的interceptors多次執行問題解決
- vue中如何簡單封裝axios淺析
- 如何使用TS對axios的進行簡單封裝