自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Axios 功能擴(kuò)展之Axios-Retry 源碼閱讀筆記

開發(fā) 前端
通過對(duì) axios-retry 這一周下載量 100w+ 的三方庫(kù)來學(xué)習(xí)下其功能設(shè)計(jì),工具庫(kù)項(xiàng)目的發(fā)包策略,并借此拋磚引玉,以提升我們的編碼設(shè)計(jì)能力!

[[436191]]

前兩天分析了 Axios 的源碼設(shè)計(jì)🔗,其中的攔截器(interceptor)為擴(kuò)展 Axios 留下了入口,在工作中我們也時(shí)常會(huì)擴(kuò)展 Axios,例如:取消重復(fù)請(qǐng)求、權(quán)限驗(yàn)證、失敗重試等。

那么如何設(shè)計(jì)實(shí)現(xiàn)一個(gè)好的攔截器來擴(kuò)展 Axios?

通過對(duì) axios-retry 這一周下載量 100w+ 的三方庫(kù)來學(xué)習(xí)下其功能設(shè)計(jì),工具庫(kù)項(xiàng)目的發(fā)包策略,并借此拋磚引玉,以提升我們的編碼設(shè)計(jì)能力!

  • Github: https://github.com/softonic/axios-retry
  • NPM: https://www.npmjs.com/package/axios-retry

一、工具庫(kù)的 package.json 寫法

看一個(gè)模塊的源碼,首先先看 README.md 和 package.json 文件。

參考如上,未來我們也應(yīng)該在開發(fā)工具庫(kù)的時(shí)候需要關(guān)注以下字段:

  • files:在發(fā)包的時(shí)候發(fā)布將 es、lib 兩文件夾,以及 index.js 和 index.d.ts 文件。
  • typings:TypeScript 類型定義文件,用于在 TypeScript 編碼環(huán)境下智能類型提示,該字段亦可寫作 types。
  • main:主要入口文件,表明在項(xiàng)目中引入當(dāng)前庫(kù)時(shí)候,默認(rèn)指向的文件是 index.js
  • module:并非官方字段,打包工具約定的如果有該字段,則在例如 Rollup 和 Webpack 打包時(shí),處理指定導(dǎo)入我們庫(kù)的 ESM 版本的文件路徑。
  • exports:提供了一種方法來為不同的環(huán)境和 JavaScript 風(fēng)格顯示聲明如何引入模塊,同時(shí)限制對(duì)其內(nèi)部部分的訪問,該字段提案來自:Bare Module Specifier Resolution in node.js[1]

通過依賴字段以及 scripts 字段:

開發(fā)依賴和使用依賴

可以得知,當(dāng)前項(xiàng)目直接使用 Babel 作為打包編譯工具,通過執(zhí)行 npm run release 發(fā)包,并結(jié)合 npm scripts 的 pre 和 post 執(zhí)行生命周期依次執(zhí)行完成如下任務(wù):

npm run release 執(zhí)行的任務(wù)流程(原文鏈接可查看大圖)

更多關(guān)于 package.json 字段的功能/作用描述,可參考 package.json - NPM[2]

二、源碼分析

根據(jù) package.json 文件中關(guān)于“發(fā)包”命令相關(guān)解讀之后,可以得知 ./es/ 文件夾下的 index.mjs 為功能實(shí)現(xiàn)文件。

2.1 為什么是 .mjs 文件名后綴

Node.js 原本的模塊系統(tǒng)是 CommonJs (使用 require 和 module.exports 語(yǔ)法)。

自 Node.js 創(chuàng)建后, ECMAScript 模塊系統(tǒng) (使用 import 和 export 語(yǔ)法) 已經(jīng)變成一種標(biāo)準(zhǔn),并且 Node.js 已經(jīng)加入并實(shí)現(xiàn)支持 ES 模塊系統(tǒng)。

Node.js 將 *.cjs 文件當(dāng)作 CommonJS 模塊, *.mjs 文件當(dāng)作 ECMAScript 模塊。它會(huì)將 .js 文件視為項(xiàng)目的默認(rèn)模塊系統(tǒng),除非 package.json 聲明 "type": "module",否則就是 CommonJS。

2.2 axios-retry 的用法

axios-retry 對(duì)外導(dǎo)出 axiosRetry() 方法:

注入攔截器

通過對(duì) axios 單例添加“攔截器”,來擴(kuò)展實(shí)現(xiàn)自動(dòng)重試網(wǎng)絡(luò)請(qǐng)求功能。

axios-retry 主要接受兩個(gè)參數(shù),第一個(gè)是 axios 實(shí)例,第二個(gè)是 axios-retry 的配置 defaultOptions:

  1. defaultOptions: { 
  2.     retries?: number; // 自動(dòng)重試次數(shù) 
  3.     shouldResetTimeout?: boolean; // 是否重置“超時(shí)時(shí)間” 
  4.     retryCondition?: Function; // 重試的條件,可傳入自定義判斷函數(shù) 
  5.     retryDelay?: Function;  // 重試請(qǐng)求的間隔時(shí)間的函數(shù) 

功能配置看起來挺完善的,難怪那么受歡迎。

2.3 請(qǐng)求攔截器設(shè)計(jì)&實(shí)現(xiàn)

在請(qǐng)求攔截器中會(huì)做狀態(tài)初始化,更新請(qǐng)求次數(shù):

  1. axios.interceptors.request.use((config) => { 
  2.   const currentState = getCurrentState(config); 
  3.   // 設(shè)置上次請(qǐng)求的時(shí)間 
  4.   // 思考🤔:為什么不放到 getCurrentState() 函數(shù)內(nèi)一起設(shè)置? 
  5.   currentState.lastRequestTime = Date.now(); 
  6.   return config; 
  7. }); 
  1. /** 
  2.  * 初始化并返回給定“請(qǐng)求”和“配置”的重試狀態(tài) 
  3.  * @param  {AxiosRequestConfig} config 
  4.  * @return {Object} 
  5.  */ 
  6. function getCurrentState(config) { 
  7.   // 從 config 獲取狀態(tài) 
  8.   const currentState = config[namespace] || {}; 
  9.   // 記錄當(dāng)前請(qǐng)求的次數(shù) 
  10.   currentState.retryCount = currentState.retryCount || 0; 
  11.   // 更新/寫入 config 中當(dāng)前請(qǐng)求狀態(tài) 
  12.   config[namespace] = currentState; 
  13.   return currentState; 

通過對(duì) axios config 注入 axios-retry 字段作為存儲(chǔ)請(qǐng)求狀態(tài)的字段,在 axios 的請(qǐng)求執(zhí)行鏈中,可隨時(shí)從 axios config 中拿到當(dāng)前請(qǐng)求狀態(tài)。

另外,我們看到請(qǐng)求攔截器中并沒有設(shè)置 reject 的函數(shù),或許這里可以添加針對(duì) reject 響應(yīng)函數(shù),用于在發(fā)生請(qǐng)求異常后,可直接不需要重試請(qǐng)求,因?yàn)殄e(cuò)誤的請(qǐng)求配置必然是無意義的網(wǎng)絡(luò)請(qǐng)求,重試請(qǐng)求也是無意義的,直接中斷退出請(qǐng)求執(zhí)行鏈。

關(guān)于退出 Promise 執(zhí)行鏈,提供幾個(gè)參考的討論:

  • 從如何停掉 Promise 鏈說起[3]
  • Promise 的鏈?zhǔn)秸{(diào)用與中止[4]

2.4 響應(yīng)攔截器設(shè)計(jì)&實(shí)現(xiàn)

在攔截器中,只響應(yīng) reject 函數(shù),也就是只在 axios 響應(yīng)階段發(fā)生錯(cuò)誤(拋出異常)的時(shí)候,才會(huì)執(zhí)行當(dāng)前攔截器。

  1. axios.interceptors.response.use(null, async (error) => { 
  2.   const { config } = error; 
  3.  
  4.   // 讀取不到 config,則退出,可能是一些其他異常情況 
  5.   // 例如:主動(dòng)取消請(qǐng)求,是直接拋出的錯(cuò)誤 
  6.   if (!config) { 
  7.     return Promise.reject(error); 
  8.   } 
  9.  
  10.   // 從 defaultOptions 讀取并設(shè)置默認(rèn)值 
  11.   const { 
  12.     retries = 3, // 默認(rèn)自動(dòng)重試 3 次 
  13.     retryCondition = isNetworkOrIdempotentRequestError, 
  14.     retryDelay = noDelay, 
  15.     shouldResetTimeout = false 
  16.   } = getRequestOptions(config, defaultOptions); 
  17.  
  18.   const currentState = getCurrentState(config); 
  19.  
  20.   // 判斷是否需要重試 
  21.   if (await shouldRetry(retries, retryCondition, currentState, error)) { 
  22.     // 需要的話,則 currentState 需要更新重試次數(shù) 
  23.     currentState.retryCount += 1; 
  24.     const delay = retryDelay(currentState.retryCount, error); 
  25.  
  26.     // Axios 合并默認(rèn)配置失敗,因?yàn)檠h(huán)結(jié)構(gòu) 
  27.     // 參考 issue: https://github.com/mzabriskie/axios/issues/370 
  28.     fixConfig(axios, config); 
  29.  
  30.     // shouldResetTimeout 默認(rèn)為 false 
  31.     // 根據(jù)實(shí)際請(qǐng)求的時(shí)間,并比較 config.timeout,選最大值來設(shè)置的超時(shí)時(shí)間 
  32.     if (!shouldResetTimeout && config.timeout && currentState.lastRequestTime) { 
  33.       const lastRequestDuration = Date.now() - currentState.lastRequestTime; 
  34.       // Minimum 1ms timeout (passing 0 or less to XHR means no timeout) 
  35.       // 設(shè)置超時(shí)時(shí)間最小 1ms(認(rèn)為 <= 0 的 XHR 請(qǐng)求不算超時(shí)) 
  36.       config.timeout = Math.max(config.timeout - lastRequestDuration - delay, 1); 
  37.     } 
  38.  
  39.     config.transformRequest = [(data) => data]; 
  40.      
  41.     // 常見的 Promise 延時(shí)的寫法(sleep) 
  42.     // 重新發(fā)起請(qǐng)求,調(diào)用 axios(config) 
  43.     // 因?yàn)闊o論何種類型請(qǐng)求,都會(huì)被標(biāo)準(zhǔn)化為 axios(config) 
  44.     // 在應(yīng)用層 axios.prototye.request 做了兼容轉(zhuǎn)換 
  45.     return new Promise((resolve) => setTimeout(() => resolve(axios(config)), delay)); 
  46.   } 
  47.  
  48.   return Promise.reject(error); 
  49. }); 

總結(jié)

這是針對(duì) axios 源碼分析文章的一個(gè)補(bǔ)充,作為常見對(duì)于 axios 的功能擴(kuò)展,失敗重試 axios-retry 算是一個(gè)比較好的例子,可以作為之后擴(kuò)展 axios 功能的一個(gè)模板。

另外,axios-retry 中通過 Babel 直接打包,以及其借助 NPM scripts 的生命周期,將測(cè)試、更新版本,打包構(gòu)建、發(fā)布、Git push串聯(lián)起來,也是值得借鑒之處。

在文中有提到,在請(qǐng)求攔截器中可以,添加針對(duì)“發(fā)起網(wǎng)絡(luò)請(qǐng)求”前的錯(cuò)誤處理,如果發(fā)生錯(cuò)誤,直接中斷重試過程,避免錯(cuò)誤的請(qǐng)求多次發(fā)起,節(jié)省計(jì)算資源,可以動(dòng)手嘗試實(shí)現(xiàn)一下。

當(dāng)然,是否需要重試請(qǐng)求,在響應(yīng)攔截器中通過 shouldRetry() 函數(shù)來保證了,但在 axios 請(qǐng)求執(zhí)行鏈上,響應(yīng)攔截器始終是需要通過發(fā)起網(wǎng)絡(luò)請(qǐng)求(dispachRequest() 事件)后才會(huì)執(zhí)行,所以這個(gè)嘗試還是可以研究研究🧐,對(duì)于搞懂 Promise 執(zhí)行鏈大有裨益。

參考資料

[1]Bare Module Specifier Resolution in node.js:

https://github.com/jkrems/proposal-pkg-exports/

[2]package.json - NPM:

https://docs.npmjs.com/cli/v8/configuring-npm/package-json

[3]從如何停掉 Promise 鏈說起:

https://github.com/xieranmaya/blog/issues/5

[4]Promise 的鏈?zhǔn)秸{(diào)用與中止:

https://cnodejs.org/topic/58385d4927d001d606ac197d

 

責(zé)任編輯:姜華 來源: DYBOY
相關(guān)推薦

2021-11-19 07:54:59

Axios網(wǎng)絡(luò)源碼

2013-12-24 10:05:04

memcached

2018-07-30 16:31:00

javascriptaxioshttp

2014-07-03 15:40:09

Apache Spar

2021-09-04 23:27:58

Axios源碼流程

2022-02-14 10:16:22

Axios接口HTTP

2025-03-25 08:40:00

前端開發(fā)Axios

2020-12-03 08:14:45

Axios核心Promise

2021-11-17 08:24:47

Vue3 插件Vue應(yīng)用

2021-09-09 10:23:08

GinNetHttp

2020-10-20 09:12:57

axios核心原理

2021-04-22 05:37:14

Axios 開源項(xiàng)目HTTP 攔截器

2021-06-02 05:41:48

項(xiàng)目實(shí)踐Axiosaxios二次封裝

2023-05-05 00:08:37

AxiosAlova開發(fā)

2016-09-20 10:15:49

LaravelPHPContainer

2016-09-20 10:26:25

LaravelPHPMiddleware

2021-07-27 14:50:15

axiosHTTP前端

2012-03-01 09:38:43

GoogleChrome

2021-04-12 05:55:29

緩存數(shù)據(jù)Axios

2022-06-13 14:18:39

電源管理子系統(tǒng)耗電量服務(wù)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)