Axios源码深度剖析 - 替代$.ajax,成为xhr的新霸主

Posted 小贼先生

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Axios源码深度剖析 - 替代$.ajax,成为xhr的新霸主相关的知识,希望对你有一定的参考价值。

 

前戏

在正式开始axios讲解前,让我们先想想,如何对现有的$.ajax进行简单的封装,就可以直接使用原声Promise了?  

 1 let axios = function(config){
 2   return new Promise((res, rej) => {
 3 
 4     // 发送ajax请求,一般使用$.ajax()
 5     ajax({
 6       ...config,
 7       success(data){
 8         res(data);
 9       },
10       error(e){
11         rej(e);
12       }
13     })
14   })
15 }

然后就可以 axios(...).then().catch()的使用了。

 

通过上面简单代码,我们就已经清楚了axios基本的实现原理:

内部通过Promise对XHR对象及其事件进行封装,

然后外部就可以通过操作Promise的方式进行异步处理了。

 

是不是很简单,那接下来就开始我们正式的讲解吧

 

为url、data、headers添加统一处理方法

 1 /**
 2  * 2,为url、data、headers添加统一处理方法
 3  */
 4 let axios = function(config) {
 5   return new Promise((res, rej) => {
 6 
 7     // 为url、data、headers添加统一处理方法
 8     // (此处只为展示大概架构,暂时不必关心方法的实现,下面会做详细介绍)
 9     let { url, method, data, params, headers } = config;
10     url = buildURL(combineURLs(config.baseURL, url), params);
11     headers = merge(headers.common, headers);
12     data = transformData(data, headers);
13 
14     // 发送ajax请求,一般使用$.ajax()
15     ajax({
16       ...config,
17       url,
18       data,
19       headers,
20       success(data) {
21         res(data);
22       },
23       error(e) {
24         rej(e);
25       }
26     })
27   })
28 }

 

放弃ajax方法,对原生XHR进行简单封装

/**
 * 3,放弃ajax方法,对原生XHR进行简单封装
 */
let axios = function (config) {
  // 默认配置项
  let defaultConfig = {
    method: ‘get‘,
    responseType: ‘json‘,
    timeout: 0,
  }

  return new Promise((res, rej) => {

    config = merge(defaultConfig, config);

    // 为url、data、headers添加统一处理方法
    let { url, method, data, params, headers } = config;
    url = buildURL(combineURLs(config.baseURL, url), params);
    headers = merge(headers.common, headers);
    data = transformData(data, headers);
   
    // 创建xhr实例
    var xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.responseType = config.responseType;
    xhr.timeout = config.timeout;
    request.addEventListener(‘progress‘, config.onDownloadProgress);
    xhr.onload = function () {
      // 先要判断 xhr.status逻辑
      res(xhr.response)
    };
    xhr.onerror = function () {
      rej(‘Network Error‘)
    };
    xhr.ontimeout = function handleTimeout() {
      rej(‘timeout‘);
    };
    xhr.send(data);
    
  })
}

 

改用class来实现一个简单的axios

 1 /**
 2  * 4,改用class来实现一个简单的axios
 3  */
 4 class Axios {
 5   constructor(instanceConfig){
 6     this.defaults = instanceConfig;
 7   }
 8   request(config){
 9 
10     this.config = {
11       ...this.defaults,
12       ...config,
13     }
14     this.dispatchRequest();
15 
16   }
17   dispatchRequest(){
18     // 为url、data、headers添加统一处理方法
19     let { url, method, data, params, headers } = this.config;
20     url = buildURL(combineURLs(config.baseURL, url), params);
21     headers = merge(headers.common, headers);
22     data = transformData(data, headers);
23 
24     // 创建xhr实例
25     var xhr = new XMLHttpRequest();
26     xhr.open(method, url);
27     xhr.responseType = config.responseType;
28     xhr.timeout = config.timeout;
29     request.addEventListener(‘progress‘, config.onDownloadProgress);
30     xhr.onload = function () {
31       // 先要判断 xhr.status逻辑
32       res(xhr.response)
33     };
34     xhr.onerror = function () {
35       rej(‘Network Error‘)
36     };
37     xhr.ontimeout = function handleTimeout() {
38       rej(‘timeout‘);
39     };
40     xhr.send(data);
41   }
42 }
43 
44 // 默认配置项
45 let defaultConfig = {
46   method: ‘get‘,
47   responseType: ‘json‘,
48   timeout: 0,
49 }
50 
51 // 创建axios实例,使axios具有 axios()直接调用方式和 axios.request()调用方式
52 function createInstance(defaultConfig){
53   let context = new Axios(defaultConfig);
54   let instance = Axios.prototype.request.bind(context);
55   extend(instance, Axios.prototype, context);
56   extend(instance, context);
57 }
58 
59 let axios = createInstance(defaultConfig);

 

添加get、post、put等简写用法

/**
 * 5, 添加get、post、put等简写用法
 */
class Axios {
  constructor(instanceConfig) {
    this.defaults = instanceConfig;
  }
  request(config) {

    this.config = {
      ...this.defaults,
      ...config,
    }
    this.dispatchRequest();

  }
  dispatchRequest() {
    // 为url、data、headers添加统一处理方法
    let { url, method, data, params, headers } = this.config;
    url = buildURL(combineURLs(config.baseURL, url), params);
    headers = merge(headers.common, headers);
    data = transformData(data, headers);

    // 创建xhr实例
    var xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.responseType = config.responseType;
    xhr.timeout = config.timeout;
    request.addEventListener(‘progress‘, config.onDownloadProgress);
    xhr.onload = function () {
      // 先要判断 xhr.status逻辑
      res(xhr.response)
    };
    xhr.onerror = function () {
      rej(‘Network Error‘)
    };
    xhr.ontimeout = function handleTimeout() {
      rej(‘timeout‘);
    };
    xhr.send(data);
  }
}
let noDataMethods = [‘delete‘, ‘get‘, ‘head‘, ‘options‘];
let hasDataMethods = [‘post‘, ‘put‘, ‘patch‘];
for (const method of noDataMethods) {
  Axios.prototype[method] = function (url, config = {}){
    return this.request({
      ...config,
      method: method,
      url: url,
    });
  } 
}
for (const method of hasDataMethods) {
  Axios.prototype[method] = function (url, data, config = {}) {
    return this.request({
      ...config,
      method: method,
      url: url,
      data,
    });
  }
}

// 默认配置项
let defaultConfig = {
  method: ‘get‘,
  responseType: ‘json‘,
  timeout: 0,
}

// 创建axios实例,使axios具有 axios()直接调用方式和 axios.request()调用方式
function createInstance(defaultConfig) {
  let context = new Axios(defaultConfig);
  let instance = Axios.prototype.request.bind(context);
  extend(instance, Axios.prototype, context);
  extend(instance, context);
}

let axios = createInstance(defaultConfig);

 

增加拦截功能,在请求前修改配置和数据,在请求后修改返回结果

 1 /**
 2  * 6,增加拦截功能,在请求前修改配置和数据,在请求后修改返回结果
 3  */
 4 class Axios {
 5   constructor(instanceConfig) {
 6     this.defaults = instanceConfig;
 7   }
 8   request(config) {
 9 
10     this.config = {
11       ...this.defaults,
12       ...config,
13     }
14     var chain = [dispatchRequest, undefined];
15     var promise = Promise.resolve(config);
16 
17   }
18   dispatchRequest(config) {
19     // 为url、data、headers添加统一处理方法
20     let { url, method, data, params, headers } = config;
21     url = buildURL(combineURLs(config.baseURL, url), params);
22     headers = merge(headers.common, headers);
23     data = transformData(data, headers);
24 
25     // 创建xhr实例
26     var xhr = new XMLHttpRequest();
27     xhr.open(method, url);
28     xhr.responseType = config.responseType;
29     xhr.timeout = config.timeout;
30     request.addEventListener(‘progress‘, config.onDownloadProgress);
31     xhr.onload = function () {
32       // 先要判断 xhr.status逻辑
33       res(xhr.response)
34     };
35     xhr.onerror = function () {
36       rej(‘Network Error‘)
37     };
38     xhr.ontimeout = function handleTimeout() {
39       rej(‘timeout‘);
40     };
41     xhr.send(data);
42   }
43 }
44 let noDataMethods = [‘delete‘, ‘get‘, ‘head‘, ‘options‘];
45 let hasDataMethods = [‘post‘, ‘put‘, ‘patch‘];
46 for (const method of noDataMethods) {
47   Axios.prototype[method] = function (url, config = {}) {
48     return this.request({
49       ...config,
50       method: method,
51       url: url,
52     });
53   }
54 }
55 for (const method of hasDataMethods) {
56   Axios.prototype[method] = function (url, data, config = {}) {
57     return this.request({
58       ...config,
59       method: method,
60       url: url,
61       data,
62     });
63   }
64 }
65 
66 // 默认配置项
67 let defaultConfig = {
68   method: ‘get‘,
69   responseType: ‘json‘,
70   timeout: 0,
71 }
72 
73 // 创建axios实例,使axios具有 axios()直接调用方式和 axios.request()调用方式
74 function createInstance(defaultConfig) {
75   let context = new Axios(defaultConfig);
76   let instance = Axios.prototype.request.bind(context);
77   extend(instance, Axios.prototype, context);
78   extend(instance, context);
79 }
80 
81 let axios = createInstance(defaultConfig);

 

 

 

// 其他:
// 撤销请求abort
// 添加 xsrf header

 

 

未完待续...

 



以上是关于Axios源码深度剖析 - 替代$.ajax,成为xhr的新霸主的主要内容,如果未能解决你的问题,请参考以下文章

唯一插件化RePlugin源码及原理深度剖析--工程职责及大纲

AJAX技术分享深度剖析AJAX全记录

Flutter Dio源码分析--深度剖析

mybatis源码级别深度剖析

libevent源码深度剖析

我有《Netty源码剖析与实战(完结)》课程网盘分享给你