前端小糾結-axios.js的實戰經驗

原文地址javascript

總結一下使用Axios.js的遇到的注意事項,若有不對歡迎指正。java

本文不是一篇分析源碼的文章,若是須要看源碼解析看這裏axios實例應用及源碼剖析 - xhr篇 (走心教程),寫的很是不錯。ios

axios.js的流程圖

先放張圖鎮樓,後續的問題須要多看看這張圖。git

來自走心教程

axios.js版本問題

以前沒有注意官網說明,後來升級了版本發現報錯,馬上把官網的文檔翻了一下,發現Semver章節有描述,github

直到v1.0以前,當發佈一個minor版本,就表明有breaking changes了。因此升級有危險啊!!!typescript

攔截器interceptor

攔截器是axios.js的核心了,就是有了攔截器作解耦,才能把代碼組合的更優雅。json

interceptor的返回值類型

當初在寫攔截器時發現,官方給的例子,interceptor的返回值既能夠是普通的value值,又能夠是Promise。當時就疑惑了,有啥區別???axios

// 官方例子:
// Add a request interceptor
axios.interceptors.request.use(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
axios.interceptors.response.use(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });
複製代碼

具體細節看源碼分析走心篇數組

// 源碼裏面就有一行代碼
promise = promise.then(chain.shift(), chain.shift());
// 其實把上面例子翻譯一下就是這樣的
// Add a request interceptor
promise = promise.then(function (config) {
    // Do something before request is sent
    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
promise = promise.then(function (response) {
    // Do something with response data
    return response;
  }, function (error) {
    // Do something with response error
    return Promise.reject(error);
  });
複製代碼

是否是瞬間就明白了攔截器的return值,其實能夠是普通的value值,或者Promise對象。promise

返回Promise就有不少想法了,咱們能夠在攔截器interceptor中作異步操做,終於能夠隨心所欲了 :)。

interceptor的執行順序

這個問題呢,是我在使用過程當中糾結的一個問題,我想作一個功能,就是當Content-Type: application/x-www-form-urlencoded;charset=utf-8時候,自動把data使用qs.stringify(後來知道這個功能其實使用transformer更合適)。

// axios/lib/core/Axios.js
  // 核心邏輯就是這段
  var chain = [dispatchRequest, undefined]; // 正中間的是發送請求的攔截器
  var promise = Promise.resolve(config);

  // request 攔截器unshift,添加到數組的頭部
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
  // response 攔截器 push到數組尾部
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  while (chain.length) {
    // 循環執行攔截器
    promise = promise.then(chain.shift(), chain.shift());
  }
複製代碼

上述代碼其實就是作了兩件事情:

  • 把攔截器放入數組chain中

    1. this.interceptors.request攔截器是從chain的頭部放入;this.interceptors.request其實也是數組,因此request數組中最後放入的interceptor卻在chain數組的最前面,和axios.interceptors.request.use時候的順序相反。
    2. this.interceptors.response攔截器是放入chain的尾部,因此interceptor順序和添加axios.interceptors.response.use時候的順序相同
  • 循環執行chain中的攔截器

    request攔截器,最後添加的,最早執行

    response攔截器,按照添加的順序執行

注意:若是你的interceptors 之間順序有***前後依賴關係***,須要特別注意。

transformer的使用

transformRequesttransformResponse都屬於Axios.js的transformer,官方宣傳的Automatic transforms for JSON data 就是經過transformResponse實現的。官方文檔寫的太簡單了,沒用寫要注意的問題。

內置的transformRequest和transformResponse

默認處理JSON

// axios/lib/defaults.js
var defaults = {
  adapter: getDefaultAdapter(),

  transformRequest: [function transformRequest(data, headers) {
    normalizeHeaderName(headers, 'Content-Type');
    if (utils.isFormData(data) ||
      utils.isArrayBuffer(data) ||
      utils.isBuffer(data) ||
      utils.isStream(data) ||
      utils.isFile(data) ||
      utils.isBlob(data)
    ) {
      return data;
    }
    if (utils.isArrayBufferView(data)) {
      return data.buffer;
    }
    if (utils.isURLSearchParams(data)) {
      setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
      return data.toString();
    }
    if (utils.isObject(data)) {
      setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
      return JSON.stringify(data);
    }
    return data;
  }],

  transformResponse: [function transformResponse(data) {
    /*eslint no-param-reassign:0*/
    if (typeof data === 'string') {
      try {
        data = JSON.parse(data);
      } catch (e) { /* Ignore */ }
    }
    return data;
  }],
};

複製代碼
// transformRequest
if (utils.isURLSearchParams(data)) {
  setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
  return data.toString();
}
if (utils.isObject(data)) {
  setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
  return JSON.stringify(data);
}

複製代碼
  • 若是data是URLSearchParams類型,axios把Content-Type修改成application/x-www-form-urlencoded;charset=utf-8,且序列化data
  • 若是data是Object,axios把Content-Type修改成application/json;charset=utf-8,且序列化data
  • transformResponse默認嘗試解析JSON

內置transformRequest和transformResponse被覆蓋

內置的transformRequesttransformResponse,很容易被覆蓋,若是你不須要內置的那就很好辦,若是你須要就須要注意下。

import axios from 'axios';
const defaults = { // 須要把默認的添加回來
  transformRequest: [].concat(axios.defaults.transformRequest, (data, headers) => {
    // do something
    return data;
  })
};

複製代碼

自動序列化data參數且修改Content-Type

注意URLSearchParams兼容性問題,若是兼容IE 11,使用qs處理下。

  • 若是data是URLSearchParams類型,axios把Content-Type修改成application/x-www-form-urlencoded;charset=utf-8,且序列化data
  • 若是data是Object,axios把Content-Type修改成application/json;charset=utf-8,且序列化data

transformer和interceptor的區別

transformer和interceptor都能在請求過程當中發揮做用,有啥區別呢?

區別:

  1. transformer和interceptor的執行的時機不一樣,看上圖
  2. transformer主要針對data(雖然也能直接改變header,可是不建議)
  3. transformer只能同步,而interceptor能夠執行異步操做
module.exports = function transformData(data, headers, fns) {
  /*eslint no-param-reassign:0*/
  utils.forEach(fns, function transform(fn) {
    data = fn(data, headers);
  });

  return data;
};

複製代碼

baseUrl配置

axios內部判斷config.url,若是是絕對路徑開始就不添加baseUrl,不然就添加

// axios/lib/core/dispatchRequest.js
// Support baseURL config
if (config.baseURL && !isAbsoluteURL(config.url)) {
  config.url = combineURLs(config.baseURL, config.url);
}
複製代碼

二進制數據注意

若是你的transformerinterceptordata有變形或者使用,必定要先判斷二進制,否則容易致使報錯。 推薦的作法就是,使用單獨的axios實例處理二進制數據。 多個axios實例之間,注意共享headers部分(這個坑,我已經踩進去了)。

總結

以上是本人使用axios.js過程當中遇到的疑惑,而後經過查看源碼的總結,若是對你有幫助,我會感到很榮幸。

axios.js相關資源

axios-retry

axios-cache-adapter

關注公衆號,發現更多精彩內容。

相關文章
相關標籤/搜索