axios源碼部分解讀

最近事情多,很久沒看jquery的源碼了(唉),而且後面排的東西越來越多,暫時應該不會繼續,下次找個機會再重新撿起來吧

因爲用到的工程裏包裝了axios,並且還踩了次坑,所以拿來看了看

自己比較感興趣的特性如下:

  1. 請求和返回的攔截
  2. 取消請求
  3. 請求代理

axios用到的包很少,只有url和follow-redirects,它的源碼在lib文件夾下,lib下面又有adapters、cancel、core和helpers四個文件夾和axios、defaults、utils三個文件。

和有些工程不同的是,lib下面的每個目錄都有readme,簡要介紹了當前的文件夾是幹什麼用的

好了,閒話說完,看主要內容

先按大體結構來看一下:

  1. 最外層
  • axios.js是入口,主要工作是創建Axios類的實例,並擴展了一些方法
  • defaults是一些默認配置項,可以直接看git上面的註釋,很齊全
  • utils裏是一些工具方法
  1. core文件夾
  • core文件夾裏的Axios纔不到100行,主要是提供了request方法,並且把’delete’, ‘get’, ‘head’, ‘options’(不帶數據)和’post’, ‘put’, ‘patch’(帶數據)這幾個方法綁到request上,request方法很簡短,但是實現了請求和返回的攔截(通過promise.then,一個數組和數組的push、unshift方法,非常清奇)
  • dispatchRequest裏處理了request data和response data的格式處理
  • 另外幾個感覺沒啥可說的
  1. cancel文件夾
    感覺大概是最難的了,CancelToken裏有個同名構造函數和一個source函數,說實話,看了很多遍都沒看懂,只理一個流程吧,先看官方給的cancel的用法,
const CancelToken = axios.CancelToken;
const source = CancelToken.source();

axios.get('/user/12345', {
  cancelToken: source.token
}).catch(function(thrown) {
  if (axios.isCancel(thrown)) {
    console.log('Request canceled', thrown.message);
  } else {
    // handle error
  }
});

axios.post('/user/12345', {
  name: 'new name'
}, {
  cancelToken: source.token
})

// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');

流程如下:
a. 執行CancelToken裏的source方法,沒幹啥,創建了一個CancelToken實例,傳遞的參數是一個函數,這個函數把自己的參數傳遞給了cancel這個變量,然後返回了cancel和這個CancelToken實例

CancelToken.source = function source() {
  var cancel;
  var token = new CancelToken(function executor(c) {
    cancel = c;
  });
  return {
    token: token,
    cancel: cancel
  };
};

然後看一眼CancelToken的構造函數幹了啥,創建了一個promise,把executor參數執行了一下,這個執行結果就是上面source方法裏面的cancel變成了executor的參數:function cancel(message),那麼後面執行source.cancel('Operation canceled by the user.');這句的時候,其實就是執行了resolvePromise(token.reason);

function CancelToken(executor) {
  if (typeof executor !== 'function') {
    throw new TypeError('executor must be a function.');
  }

  var resolvePromise;
  this.promise = new Promise(function promiseExecutor(resolve) {
    resolvePromise = resolve;
  });

  var token = this;
  executor(function cancel(message) {
    if (token.reason) {
      // Cancellation has already been requested
      return;
    }
    token.reason = new Cancel(message);
    resolvePromise(token.reason);
  });
}

b. a裏面其實還只執行到const source = CancelToken.source();這句,接下來就是執行請求,執行請求就走到了core裏的Axios.prototype.request裏,比較重要的兩句

...

var chain = [dispatchRequest, undefined];

... //這裏會插入請求和返回的攔截器

while (chain.length) {
  promise = promise.then(chain.shift(), chain.shift());
}

排除攔截器的影響,最後會執行到dispatchRequest,dispatchRequest方法的第一句就執行了判斷是否要取消請求的操作throwIfCancellationRequested(config);,這一句最終執行到

CancelToken.prototype.throwIfRequested = function throwIfRequested() {
  if (this.reason) {
    throw this.reason;
  }
};

拋出一個錯誤,結束。這是上面官方例子的取消流程,平時使用的時候,還有一種方式(同事提供),用一個變量來保存cancelToken,然後檢測這個變量是否已存在,在有多次發送請求的情況下,可以取消發送過程中的請求,這樣的流程和官方例子會有點不一樣,因爲官方例子是直接在請求前進行取消的,而這種情況會走到adapter裏面去取消(在adapter文件夾中搜索cancelToken就可以找到)。
4. adapter文件夾
兩種發送請求的方式,一種瀏覽器的ajax方式,一種是node的http方式,其中只有http方式支持請求代理,當然了,如果自己想加一個ajax方式的代理,也是可以的,模仿下代碼就行了,代理其實也不是很高深的東西,只是把請求的host和port給改了,官方實例如下:

  proxy: {
    host: '127.0.0.1',
    port: 9000,
    auth: {
      username: 'mikeymike',
      password: 'rapunz3l'
    }
  },

上面這一段直接寫在請求的配置裏就可以了,不過自己手寫的代理服務器的話,需要支持對應的請求路徑,被這個坑了一臉,比如說,我請求http://127.0.0.1:3000/api?username=123,設置代理127.0.0.1:3001,如果3001上沒有api這個路徑的監聽,是不會成功的

通過這次看axsios的代碼,大概收穫了以下這些以前沒見過的知識點:

  1. URLSearchParams(兼容性不好)
  2. XDomainRequest(已經是不支持的過時的東西了)
  3. 204狀態碼(提交form的時候不刷新頁面也不跳轉)

axios前前後後大概看了得有五六回,基本都是在看請求取消的環節,非常費解,大概是水平太差,各個文件裏的promise有點多也比較容易混淆

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章