javascript請求-axios

axios是在實際項目中應用比較多的請求插件了,提供給咱們的前置請求配置、響應攔截配置,讓咱們在實際應用中可以很好的利用;
node

爲何要看axios源碼呢?

不知道你們有沒有這種時候,當身邊的其餘人都在看源碼實現時候,本身卻無從下手,看着超級多的邏輯,逐漸失去了興趣,所以我發現我本身的看源碼切入點,「好奇」,看着項目配置中的「響應攔截」、「請求攔截」等,由心而生想要去知道這樣究竟是如何實現的呢? 帶着這種「好奇」我打開了axios的源碼。ios

在看源碼以前,必要的內容是先過一遍axios的使用方法,這樣帶着問題,會記憶深入ajax

1.配置覆蓋

axios的配置大概分爲三種axios

1.1全局配置

即在插件內部的默認配置api

  • 其實更通俗而言就是默認的配置 默認配置均存在於default.js中,

image.png

圖1 全局默認配置的位置

image.png

圖2 全局默認配置

1.2實例配置

在進行項目搭建時候,一般使用的自定義的全局配置數組

        有的時候 在一個應用中須要實例化不一樣的kxios對象,針對不一樣的接口 有利於管理,每一個實例化對象度灰有本身的配置,能夠經過全局配置進行初始化,或合併成一個新的配置項目,實例配置通常是咱們進行本身配置的。promise

const nAxios = axios.create({
  // baseURL: process.env.BASE_API,
  // timeout: 5000,
  withCredentials: true,
  headers: {'X-Requested-With': 'XMLHttpRequest'}
})
複製代碼

1.3請求配置

在進行請求時候,對單個請求進行的配置瀏覽器

        同一個實例會有一些公用的配置項目,如baseUrl,可是不少時候,不一樣的請求具體的配置是不同的,如url、method等,因此在請求的時候須要傳入的配置與實例配置進行合併;         請求配置合併通常是這樣子的?get請求的url ,後面的config都是咱們傳入的請求配置,傳入到項目中後,會merge 默認的配置,而後在進行請求;緩存

kxios.get('http://localhost:4000/',{
  baseURL:'http://localhost:4000/',
  methods:'get',
  headers:{
    'Content-Type':'333',
    'Type':333
  }
}).then((res)=>{
  console.log("請求後的then")
  console.log(res)
})
複製代碼

1.4配置優先級

請求配置>實例配置> 全局配置markdown

2.建立請求過程

2.1 建立axios

axios.create = function create(instanceConfig) {
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
複製代碼

2.2建立axios實例

image.png

圖3 請求入口主要文件

該文件提供了axios的實例構造器,即工廠函數

function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig);
  // instance 綁定axios的默認數據 instance 
  var instance = bind(Axios.prototype.request, context);
  // 複製axios的原型到實例中。此處主要是確保後續進行改變時候,不改變Axios的對象函數
  utils.extend(instance, Axios.prototype, context);
  //將實例綁定到Axios構造出得對象中
  utils.extend(instance, context);
  //instance 進行復制
  return instance;
}
複製代碼
  1. 建立Axios的實例
  2. instance 綁定axios的默認數據instance
  3. axios的原型到實例中。此處主要是確保後續進行改變時候,不改變Axios的對象函數
  4. 將實例綁定到Axios構造出得對象中

之因此選擇這樣,建立實例,而後在複製原型方法,是爲了擴展axios自己更多的調用功能,在Axios的函數中,具備複雜數據類型,保證每一個axios的實例在繼承後都有獨立的數據結構;

axios.get(url)
axios(url)
複製代碼

在外界引用了axios的方法就能進行建立axios的實例,並獲得初始的默認值 進行調用

var nAxios = axios.create({
  baseURL: process.env.BASE_API,
  timeout: 5000,
  withCredentials: true,
  headers: {'X-Requested-With': 'XMLHttpRequest'}
})
//調用函數
nAxios.get('http:locallhost:888')
複製代碼

對於nAxios.get('http:locallhost:888')的執行的過程

  • axios中進行調用get方法
  • Axios.js中對Axios的原型都附加上了請求方法,請求調用的時候直接調用時候,先進行merge請求的配置,在進行調用config的方法

2.3 merge 配置

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
    }));
  };
});
複製代碼

2.4 進行請求適配配置

請求時候進行請求的調度,即去適配請求

image.png

圖4 dispatchRequest發起請求
  • dispatchRequest.js的功能分析

image.png

圖5 dispatchRequest功能分析

2.5 發送請求的主要緣由

在瀏覽器環境中主要是經過ajax進行請求

image.png

圖6 瀏覽器中xhr發送請求

在瀏覽器中進行發送請求主要仍是以來XMLHttpRequest

image.png

圖7 xhr.js中主要功能

3.node可使用axios的緣由

咱們在實際的使用中,發現node環境和瀏覽器環境均可以使用axios,實際上是axios內部進行了適配;

  • 在default.js 中,就進行了適配的操做
//獲取默認的請求適配器
function getDefaultAdapter() {
  var adapter;
  //若是當前存在XMLHttpRequest 這個對象,則進行XHr的額適配器
  if (typeof XMLHttpRequest !== 'undefined') {
    // 瀏覽器環境下使用XML這個適配器
    adapter = require('./adapters/xhr')
  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // node 環境下使用http這個模塊適配器
    adapter = require('./adapters/http');
  }
  return adapter;
}
複製代碼

image.png

圖8 axios適用於多個平臺緣由

4.請求攔截器和響應攔截器原理

請求攔截器和響應攔截器幫助咱們在實際工做中去處理本身前置轉換需求

4.1 攔截器做用

  • 當咱們發送數據的時候,可以攔截到發送內容並進行更改內容,適用於統一性的發送信息,好比token、自定義的請求頭,經過統一封裝的request函數爲每一個請求添加統一的信息;
  • 但後期若是須要爲某些 GET 請求設置緩存時間或者控制某些請求的調用頻率的話,咱們就須要不斷修改 request 函數來擴展對應的功能。此時,若是在考慮對響應進行統一處理的話,咱們的 request 函數將變得愈來愈龐大,也愈來愈難維護
  • 咱們能夠按照功能把發送 HTTP 請求拆解成不一樣類型的子任務,好比有用於處理請求配置對象的子任務,用於發送 HTTP 請求的子任務和用於處理響應對象的子任務。當咱們按照指定的順序來執行這些子任務時,就能夠完成一次完整的 HTTP 請求。

4.2 攔截器構造函數

/axios/lib/core/InterceptorManager.js

在該文件中定義了攔截器的構造函數InterceptorManager,用於響應攔截和請求攔截的使用

function InterceptorManager() {
  this.handlers = [];
}
//增長一個響應
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};
//移除某一個
InterceptorManager.prototype.eject = function eject(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
};  

複製代碼

4.3 建立請求攔截和響應攔截

應用在Axios的構造函數中

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(), //請求攔截器
    response: new InterceptorManager() // 響應攔截器
  };
}
複製代碼

4.4 攔截器執行過程

請求攔截器和響應攔截器主要實現原理爲Promise的鏈式執行方法;

// 攔截器中間件
  //dispatchRequest爲默認的主要請求內容 dispatchRequest返回一個promise的請求對象
  var chain = [dispatchRequest, undefined];
  //讓config配置promise中,用來層級傳遞配置
  var promise = Promise.resolve(config); 
  //請求攔截器
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    //將請求攔截器插入到 chain數組的前面,插入的原理實際上是 自定義配置時候越在後配置的先執行
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
  // 響應攔截器 將響應攔截器push 到chain 的後面 這樣會後執行
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });
  //chain的結構 =[請求攔截器1,請求攔截器2,主要請求,響應攔截器1,響應攔截器2]
  //執行chain中的方法
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }
複製代碼

這樣就造成了一個攔截器的Promise隊列

image.png

圖9 攔截器隊列結構
  • 執行該隊列結束請求

5.請求過程當中的數據轉換transformData

  • 請求操做數據會在請求攔截器以前進行執行,而響應攔截器則會在響應攔截器以前進行調用,
  • 執行在promise的then中 dispatchRequest.js

5.1 transformRequest請求操做數據

image.png

圖10 請求操做數據

5.2 響應操做數據transformResponse

image.png

圖11 響應操做數據

轉換函數

module.exports = function transformData(data, headers, fns) {
  /*eslint no-param-reassign:0*/
  utils.forEach(fns, function transform(fn) {
    data = fn(data, headers);
  });
//返回轉換後的數據
  return data;
};
複製代碼

6. 一個axios的流程

建立實例的時候+調用接口數據的時候

image.png

圖12 一個axios的流程

7.取消請求

取消請求的邏輯均在axios/lib/cancel中,取消請求在配置中主要使用CancelToken進行控制,在請求中,也存在專門爲CancelToken設置的方法, 在xhr.js中

if (config.cancelToken) {
      // Handle cancellation 
      config.cancelToken.promise.then(function onCanceled(cancel) {
        if (!request) {
          return;
        }
        //中止當前的請求
        request.abort();
        //而且進行reject
        reject(cancel);
        // Clean up request
        request = null;
      });
    }
複製代碼

cancelToken的方法 中主要的一個做用在於執行阻止請求,並進行取消函數,進行下一個then的調用

8.總結

看完axios ,本身收穫仍是蠻多的,更多的是流程設計

  • 如何暴露更靈活的api供使用
  • 巧妙利用Promise的then原理增長請求和攔截控制

9.參考文檔

axios的使用

相關文章
相關標籤/搜索