手表網(wǎng)站制作照片網(wǎng)絡(luò)營(yíng)銷的核心
這兩個(gè)功能都是用攔截器實(shí)現(xiàn)。
前景提要:
ts 簡(jiǎn)易封裝 axios,統(tǒng)一 API
實(shí)現(xiàn)在 config 中配置開關(guān)攔截器
全局錯(cuò)誤處理
在構(gòu)造函數(shù)中,添加一個(gè)響應(yīng)攔截器即可。在構(gòu)造函數(shù)中注冊(cè)攔截器的好處是,無論怎么實(shí)例化封裝類,這個(gè)錯(cuò)誤攔截器都會(huì)被注冊(cè)進(jìn)實(shí)例。
其中有個(gè)注意點(diǎn),就是請(qǐng)求取消。取消請(qǐng)求會(huì)導(dǎo)致響應(yīng) promise 狀態(tài)為 rejected,這樣就會(huì)觸發(fā)響應(yīng)攔截器的 onRejected 回調(diào)。因此要單獨(dú)處理請(qǐng)求的請(qǐng)求情況,將它與請(qǐng)求錯(cuò)誤區(qū)分開來。
class HttpRequest {private readonly instance: AxiosInstance;constructor(config: MyAxiosRequestConfig) {this.instance = axios.create(config);// axios http 錯(cuò)誤處理(超出 2xx 范圍的 http 狀態(tài)碼都會(huì)觸發(fā)該函數(shù))this.instance.interceptors.response.use(null, (error: AxiosError) => {// 手動(dòng)取消請(qǐng)求會(huì)導(dǎo)致“錯(cuò)誤”觸發(fā)if (error.message === "canceled") alert("請(qǐng)求取消成功");const { response } = error;// 1. 請(qǐng)求超時(shí) && 網(wǎng)絡(luò)錯(cuò)誤單獨(dú)判斷,因?yàn)闆]有 responseif (error.message.indexOf("timeout") !== -1) alert("請(qǐng)求超時(shí)!請(qǐng)您稍后重試");if (error.message.indexOf("Network Error") !== -1) alert("網(wǎng)絡(luò)錯(cuò)誤!請(qǐng)您稍后重試");// 2. 根據(jù) http 服務(wù)器響應(yīng)的錯(cuò)誤狀態(tài)碼,做不同的處理if (response) {switch (response.status) {case 404:alert("你所訪問的資源不存在!");break;case 500:alert("服務(wù)異常!");break;default:alert("請(qǐng)求失敗!");}}// 3. 服務(wù)器結(jié)果都沒有返回(可能服務(wù)器錯(cuò)誤可能客戶端斷網(wǎng)),斷網(wǎng)處理:也可以跳轉(zhuǎn)到斷網(wǎng)頁面if (!window.navigator.onLine) alert("服務(wù)器錯(cuò)誤或者無網(wǎng)絡(luò)"); // router.replace("/500");throw error;});}
}
取消請(qǐng)求
axios 取消請(qǐng)求的方式
axios 取消請(qǐng)求有兩種 api。一種是 AbortController,一種是古老的 CancelToken ,已經(jīng)被棄用了。
AbortController - Web API 接口參考 | MDN
我們主要使用第一種方式:
- 實(shí)例化取消控制器接口,控制器對(duì)象有一個(gè)信號(hào)標(biāo)記
signal
- 將該標(biāo)記配置給 axios 同名的
signal
配置 - 控制器對(duì)象調(diào)用
abort()
方法就能取消被標(biāo)記了的請(qǐng)求。
const controller = new AbortController();axios.get('/foo/bar', {signal: controller.signal
}).then(function(response) {//...
});
// 取消請(qǐng)求
controller.abort()
封裝取消請(qǐng)求功能
取消請(qǐng)求也是個(gè)基礎(chǔ)功能,因此和全局錯(cuò)誤攔截器一樣在構(gòu)造函數(shù)中注冊(cè)攔截器。
取消請(qǐng)求:
- 為每一個(gè)請(qǐng)求生成一個(gè)控制器 controller,并添加 signal
- 維護(hù)一個(gè) map,以 url 為 key,對(duì)應(yīng)的 controller 為 value
- 要取消哪個(gè)請(qǐng)求就通過 url,獲取它的 controller 來取消
- 在全局響應(yīng)攔截器中,給所有請(qǐng)求添加 signal,并在請(qǐng)求結(jié)束后從 map 中剔除該 url 對(duì)應(yīng)的 controller
封裝類暴露兩個(gè)方法:
- 取消全部請(qǐng)求
- 根據(jù) url 取消請(qǐng)求
class HttpRequest {private readonly instance: AxiosInstance;private readonly abortControllerMap: Map<string, AbortController>;constructor(config: MyAxiosRequestConfig) {this.instance = axios.create(config);// 為每個(gè)請(qǐng)求都生成一個(gè) signal,并以 url 為 key 添加入 mapthis.instance.interceptors.request.use(config => {// 如果具體方法中設(shè)置了 signal,這里就不再添加,避免覆蓋。if (config.signal) return config;const controller = new AbortController();config.signal = controller.signal;const url = config.url || "";this.abortControllerMap.set(url, controller);return config;},(err: AxiosError) => {throw err;});// 響應(yīng)完,從map中去除 urlthis.instance.interceptors.response.use(res => {const url = res?.config.url || "";this.abortControllerMap.delete(url);return res;},(err: AxiosError) => {const url = err?.config?.url || "";this.abortControllerMap.delete(url);throw err;});}/*** 取消全部請(qǐng)求*/cancelAllRequest() {for (const [, controller] of this.abortControllerMap) {controller.abort();}this.abortControllerMap.clear();}/*** 取消指定的請(qǐng)求* (并發(fā)上傳文件的url通常是一樣的,通過url取消請(qǐng)求會(huì)取消所有上傳操作,故此方法不宜用來取消上傳請(qǐng)求)* @param url 待取消的請(qǐng)求URL*/cancelRequest(url: string | string[]) {const urlList = Array.isArray(url) ? url : [url];urlList.forEach(_url => {this.abortControllerMap.get(_url)?.abort();this.abortControllerMap.delete(_url);});}
}