axios源碼閱讀

爲了方便使用,axios對象既能作對象使用,又能作函數使用.node

axios.post('/user', {
    firstName: 'Fred',
    lastName: 'Flintstone'
  })
  .then(function (response) {
    console.log(response);
  })
  .catch(function (error) {
    console.log(error);
  });
  
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});

這一點axios是如何作到的,能夠看到instance實際上是一個綁定this的函數,調用axios就是調用context.requestios

function createInstance(){
    // 能當作函數使用的祕密
    var instance = bind(Axios.prototype.request, context);
    // 能當作對象使用的祕密
    utils.extend(instance, Axios.prototype, context);
    // 要拿到構造函數繼承的屬性
    utils.extend(instance, context);
    return instance
}

var axios = createInstance(defaults);

接下來咱們看一下request方法,全部http請求的發送都會調用Axios.prototype.request,這個函數能夠認爲是整個axios的骨架,很是重要。axios

Axios.prototype.request = function request(config) {
    // 每一個請求都會從新合成一個config,因此經過操做config對象,你能夠標識請求,作某些操做,事實上每一個axios的攔截器都能拿到config對象
    config = utils.merge(defaults, this.defaults, { method: 'get' }, config);
    
    // 掛載攔截器的主要邏輯
    var chain = [dispatchRequest, undefined];
    var promise = Promise.resolve(config);
    
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

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

  return promise;
}

從攔截器中的主要邏輯,咱們能夠獲得如下幾點:promise

  • 發送請求的整個執行順序是,requestInterceptors ——》dispatchRequest ——》responseInterceptors
  • 攔截器最初接收的對象是config,axios使用中也規定,請求的攔截器必需要返回config,這也是每一個請求攔截器的函數參數是config的緣由
  • 攔截器的執行順序與interceptors.request.use(function () {/*...*/})執行的順序有關,即先use的請求攔截器會先執行。
  • 若是攔截器中的函數時async函數,會阻塞整個攔截器鏈的執行,而transformData不會,因此若是須要對請求的數據作異步處理的話,要在攔截器中完成。

看一下,不一樣的http method是怎麼複用request方法的緩存

utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

接下來咱們看dispatchRequest的核心邏輯:異步

// 處理config...

var adapter = config.adapter || defaults.adapter;

return adapter(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);

// Transform response data
response.data = transformData(
  response.data,
  response.headers,
  config.transformResponse
);

return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
  throwIfCancellationRequested(config);

  // Transform response data
  if (reason && reason.response) {
    reason.response.data = transformData(
      reason.response.data,
      reason.response.headers,
      config.transformResponse
    );
  }
}

return Promise.reject(reason);
});

能夠看到dispatchRequest的核心邏輯大概有三步async

  • 處理config
  • 使用adapter發送請求,axios默認內置兩個adapter,一個是負責在brower發送請求的,一個是負責在node端發送請求,能夠在根文件的defaults下看到
  • 構造響應數據

因此經過dispatchRequest方法的閱讀,咱們能夠獲得如下啓示:函數

  • adapter是能夠替換的,因此若是你以爲你的xhr或http的邏輯更適合業務的須要,徹底能夠替換掉,你也徹底能夠開發出第三種adapter以處理特定狀況,好比開發一個處理緩存的adapter,事實上我如今的項目就是這樣作的。
  • 響應的攔截器接收到的是response對象

至此,咱們已經把axios的核心邏輯閱讀完畢,從中咱們也能夠看到axios的易用性和可拓展性很是強。post

尤爲是可拓展性,發送請求到接收響應的過程當中的全部部分幾乎都是可拓展的,尤爲是config,adapter,interceptor留下了不少想象的空間。this

相關文章
相關標籤/搜索