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