axios?攔截器管理類鏈?zhǔn)秸{(diào)用手寫(xiě)實(shí)現(xiàn)及原理剖析
axios庫(kù)的攔截器使用
我們知道axios庫(kù)的攔截器的使用方式如下:
// 添加一個(gè)請(qǐng)求攔截器
axios.interceptors.request.use(function (config) {
// 在發(fā)送請(qǐng)求之前可以做一些事情
return config;
}, function (error) {
// 處理請(qǐng)求錯(cuò)誤
return Promise.reject(error);
});
// 添加一個(gè)響應(yīng)攔截器
axios.interceptors.response.use(function (response) {
// 處理響應(yīng)數(shù)據(jù)
return response;
}, function (error) {
// 處理響應(yīng)錯(cuò)誤
return Promise.reject(error);
});
在 axios 對(duì)象上有一個(gè) interceptors 對(duì)象屬性,該屬性又有 request 和 response 2 個(gè)屬性,它們都有一個(gè) use 方法,use 方法支持 2 個(gè)參數(shù),第一個(gè)參數(shù)類似 Promise.then 的 resolve 函數(shù),第二個(gè)參數(shù)類似 Promise.then 的 reject 函數(shù)。我們可以在 resolve 函數(shù)和 reject 函數(shù)中執(zhí)行同步代碼或者是異步代碼邏輯。
并且我們是可以添加多個(gè)攔截器的,攔截器的執(zhí)行順序是鏈?zhǔn)揭来螆?zhí)行的方式。對(duì)于 request 攔截器,后添加的攔截器會(huì)在請(qǐng)求前的過(guò)程中先執(zhí)行;對(duì)于 response 攔截器,先添加的攔截器會(huì)在響應(yīng)后先執(zhí)行。
axios.interceptors.request.use(config => {
config.headers.test += '1'
return config
})
axios.interceptors.request.use(config => {
config.headers.test += '2'
return config
})
此外,我們也可以支持刪除某個(gè)攔截器,如下:
const myInterceptor = axios.interceptors.request.use(function () {/*...*/})
axios.interceptors.request.eject(myInterceptor)
整體設(shè)計(jì)
我們先用一張圖來(lái)展示一下攔截器工作流程:

整個(gè)過(guò)程是一個(gè)鏈?zhǔn)秸{(diào)用的方式,并且每個(gè)攔截器都可以支持同步和異步處理,我們自然而然地就聯(lián)想到使用 Promise 鏈的方式來(lái)實(shí)現(xiàn)整個(gè)調(diào)用過(guò)程。
在這個(gè) Promise 鏈的執(zhí)行過(guò)程中,請(qǐng)求攔截器 resolve 函數(shù)處理的是 config 對(duì)象,而相應(yīng)攔截器 resolve 函數(shù)處理的是 response 對(duì)象。
在了解了攔截器工作流程后,我們先要?jiǎng)?chuàng)建一個(gè)攔截器管理類,允許我們?nèi)ヌ砑?刪除和遍歷攔截器。
攔截器管理類實(shí)現(xiàn)
根據(jù)需求,axios 擁有一個(gè) interceptors 對(duì)象屬性,該屬性又有 request 和 response 2 個(gè)屬性,它們對(duì)外提供一個(gè) use 方法來(lái)添加攔截器,我們可以把這倆屬性看做是一個(gè)攔截器管理對(duì)象。
use 方法支持 2 個(gè)參數(shù),第一個(gè)是 resolve 函數(shù),第二個(gè)是 reject 函數(shù),對(duì)于 resolve 函數(shù)的參數(shù),請(qǐng)求攔截器是 AxiosRequestConfig 類型的,而響應(yīng)攔截器是 AxiosResponse 類型的;而對(duì)于 reject 函數(shù)的參數(shù)類型則是 any 類型的。
根據(jù)上述分析,我們先來(lái)定義一下攔截器管理對(duì)象對(duì)外的接口。
接口定義
這里我們定義了 AxiosInterceptorManager 泛型接口,因?yàn)閷?duì)于 resolve 函數(shù)的參數(shù),請(qǐng)求攔截器和響應(yīng)攔截器是不同的。
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
}
代碼實(shí)現(xiàn)
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() {
// 攔截器數(shù)組
this.interceptors = []
}
// 收集攔截器
use(resolved: ResolvedFn<T>, rejected?: RejectedFn): number {
this.interceptors.push({
resolved,
rejected
})
return this.interceptors.length - 1
}
// 遍歷用戶寫(xiě)的攔截器,并執(zhí)行fn函數(shù)把攔截器作為參數(shù)傳入
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
}
}
}
我們定義了一個(gè) InterceptorManager 泛型類,內(nèi)部維護(hù)了一個(gè)私有屬性 interceptors,它是一個(gè)數(shù)組,用來(lái)存儲(chǔ)攔截器。該類還對(duì)外提供了 3 個(gè)方法,其中 use 接口就是添加攔截器到 interceptors 中,并返回一個(gè) id 用于刪除;
forEach 接口就是遍歷 interceptors 用的,它支持傳入一個(gè)函數(shù),遍歷過(guò)程中會(huì)調(diào)用該函數(shù),并把每一個(gè) interceptor 作為該函數(shù)的參數(shù)傳入;eject 就是刪除一個(gè)攔截器,通過(guò)傳入攔截器的 id 刪除。
鏈?zhǔn)秸{(diào)用實(shí)現(xiàn)
當(dāng)我們實(shí)現(xiàn)好攔截器管理類,接下來(lái)就是在 Axios 中定義一個(gè) 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 個(gè)屬性,一個(gè)請(qǐng)求攔截器管理類實(shí)例,一個(gè)是響應(yīng)攔截器管理類實(shí)例。我們?cè)趯?shí)例化 Axios 類的時(shí)候,在它的構(gòu)造器去初始化這個(gè) interceptors 實(shí)例屬性。
接下來(lái),我們修改 request 方法的邏輯,添加攔截器鏈?zhǔn)秸{(diào)用的邏輯:
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
}
// 定義一個(gè)數(shù)組,這個(gè)數(shù)組就是要執(zhí)行的任務(wù)鏈,默認(rèn)有一個(gè)真正發(fā)送請(qǐng)求的任務(wù)
const chain: PromiseChain[] = [{
resolved: dispatchRequest,
rejected: undefined
}]
// 把用戶定義的請(qǐng)求攔截器存放到任務(wù)鏈中,請(qǐng)求攔截器最后注冊(cè)的最先執(zhí)行,所以使用unshift方法
this.interceptors.request.forEach(interceptor => {
chain.unshift(interceptor)
})
// 把響應(yīng)攔截器存放到任務(wù)鏈中
this.interceptors.response.forEach(interceptor => {
chain.push(interceptor)
})
// 利用config初始化一個(gè)promise
let promise = Promise.resolve(config)
// 遍歷任務(wù)鏈
while (chain.length) {
// 取出任務(wù)鏈的首個(gè)任務(wù)
const { resolved, rejected } = chain.shift()!
// resolved的執(zhí)行時(shí)機(jī)是就是上一個(gè)promise執(zhí)行resolve()的時(shí)候,這樣就形成了鏈?zhǔn)秸{(diào)用
promise = promise.then(resolved, rejected)
}
return promise
}
首先,構(gòu)造一個(gè) PromiseChain 類型的數(shù)組 chain,并把 dispatchRequest 函數(shù)賦值給 resolved 屬性;接著先遍歷請(qǐng)求攔截器插入到 chain 的前面;然后再遍歷響應(yīng)攔截器插入到 chain 后面。
接下來(lái)定義一個(gè)已經(jīng) resolve 的 promise,循環(huán)這個(gè) chain,拿到每個(gè)攔截器對(duì)象,把它們的 resolved 函數(shù)和 rejected 函數(shù)添加到 promise.then 的參數(shù)中,這樣就相當(dāng)于通過(guò) Promise 的鏈?zhǔn)秸{(diào)用方式,實(shí)現(xiàn)了攔截器一層層的鏈?zhǔn)秸{(diào)用的效果。
注意我們攔截器的執(zhí)行順序,對(duì)于請(qǐng)求攔截器,先執(zhí)行后添加的,再執(zhí)行先添加的;而對(duì)于響應(yīng)攔截器,先執(zhí)行先添加的,后執(zhí)行后添加的。
以上就是axios 攔截器管理類鏈?zhǔn)秸{(diào)用手寫(xiě)實(shí)現(xiàn)及原理剖析的詳細(xì)內(nèi)容,更多關(guān)于axios 攔截器管理類鏈?zhǔn)秸{(diào)用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
詳解微信小程序如何實(shí)現(xiàn)類似ChatGPT的流式傳輸
這篇文章主要為大家介紹了微信小程序如何實(shí)現(xiàn)類似ChatGPT的流式傳輸示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
JS常用正則表達(dá)式超全集(密碼強(qiáng)度校驗(yàn),金額校驗(yàn),IE版本,IPv4,IPv6校驗(yàn))
網(wǎng)上有很多關(guān)于JS常用正則表達(dá)式的文章很全但今天為大家分享一些最新,且非常有用的正則表達(dá)式其中有密碼強(qiáng)度校驗(yàn),金額校驗(yàn),IE版本,IPv4,IPv6校驗(yàn)等2020-02-02
微信小程序 詳解下拉加載與上拉刷新實(shí)現(xiàn)方法
這篇文章主要介紹了微信小程序 詳解下拉加載與上拉刷新實(shí)現(xiàn)方法的相關(guān)資料,這里介紹了兩種實(shí)現(xiàn)方法,需要的朋友可以參考下2017-01-01
Javascript基礎(chǔ)知識(shí)中關(guān)于內(nèi)置對(duì)象的知識(shí)
這篇文章主要介紹了Javascript基礎(chǔ)知識(shí)中關(guān)于內(nèi)置對(duì)象的相關(guān)知識(shí)的相關(guān)資料,需要的朋友可以參考下面小編薇大家?guī)?lái)的精彩文章2021-09-09

