# Axios 源码分析

# 简介

  • axios 是一个基于 Promise 网络请求库,作用于 NodeJS 和 浏览器中。
    在服务端使用原生 nodejs 的 http 模块,浏览器端使用 XMLHttpRequests

# 特性

  • 从浏览器创建 XMLHttpRequests

  • 从 nodejs 创建 http 请求

  • 支持 Promise Api

  • 拦截请求和响应

  • 转换 请求和响应 数据

  • 取消请求

  • 自动转换 JSON 数据

  • 客户端支持防御 XSRF

# axios 内部处理流程

  • 开始: create axios 构造函数

  • 执行请求拦截器: new InterceptorManager() request

  • 派发请求: dispatchRequest()

  • 转换请求数据: transformData for request data

  • 适配器处理请求: adapter 区分浏览器和Node环境

  • 转换响应数据: transformData for response data

  • 执行响应拦截器: new InterceptorManager response

  • 结束: return axios

# 文件目录

lib
  -- adapters # 适配器
    -- xhr.js # 浏览器环境
    -- http.js # node环境
  -- cancel
  -- core # 核心代码
    -- Axios.js # 实例初始化
    -- dispatchRequest.js # 调用适配器发起请求
    -- InterceptorManager.js # 拦截器处理
    -- mergeConfig.js # 合并配置
    -- transformData.js # 转换数据
    -- helpers
  -- axios.js # 入口文件
  -- defaults.js # 默认配置
  -- utils.js # 工具方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 入口文件 axios.js

/**
 * Create a new instance of Axios
 *
 * @param {Object} instanceConfig The default config for the instance
 */
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

var Axios = require('./core/Axios');

/**
 * Create an instance of Axios
 *
 * @param {Object} defaultConfig The default config for the instance
 * @return {Axios} A new instance of Axios
 */
function createInstance(defaultConfig) {
  // 创建 axios 实例 
  var context = new Axios(defaultConfig);
  // 将 实例 instance 指向 request 方法
  var instance = bind(Axios.prototype.request, context);

  // Copy axios.prototype to instance
  // 将 prototype 上的方法扩展到 instance 上  指定上下文是 context 
  utils.extend(instance, Axios.prototype, context);

  // Copy context to instance
  // 将 context 上的方法扩展到 instance 上
  utils.extend(instance, context);

  // Factory for creating new instances
  // 添加 create 方法 参数是 默认配置和自定义配置
  instance.create = function create(instanceConfig) {
    return createInstance(mergeConfig(defaultConfig, instanceConfig));
  };

  return instance;
}

// Create the default instance to be exported
var axios = createInstance(defaults);

// Expose Axios class to allow class inheritance
axios.Axios = Axios;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  • 当调用 axios() 时,实际上是调用了 createInstance() 返回的一个指向 Axios.prototype.request 的函数

  • 通过添加 create 方法支持用户自定义配置创建,并最终也执行了 Axios.prototype.request 方法

# Axios.prototype.request

  • 合并配置

  • 设置 config.method

  • 请求、响应拦截器

  • Promise 调用链执行

  • 派发请求 dispatchRequest

/**
 * Create a new instance of Axios
 *
 * @param {Object} instanceConfig The default config for the instance
 */
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  // 拦截器
  this.interceptors = {
    request: new InterceptorManager(), // 请求拦截器
    response: new InterceptorManager() // 响应拦截器
  };
}

/**
 * Dispatch a request
 *
 * @param {Object} config The config specific for this request (merged with this.defaults)
 */
Axios.prototype.request = function request(config) {
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  if (typeof config === 'string') {
    config = arguments[1] || {};
    config.url = arguments[0];
  } else {
    config = config || {};
  }

  // 合并配置 
  config = mergeConfig(this.defaults, config);

  // Set config.method
  if (config.method) {
    config.method = config.method.toLowerCase();
  } else if (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get';
  }

  var transitional = config.transitional;

  if (transitional !== undefined) {
    validator.assertOptions(transitional, {
      silentJSONParsing: validators.transitional(validators.boolean),
      forcedJSONParsing: validators.transitional(validators.boolean),
      clarifyTimeoutError: validators.transitional(validators.boolean)
    }, false);
  }

  // filter out skipped interceptors
  // 请求拦截 队列
  var requestInterceptorChain = [];
  var synchronousRequestInterceptors = true;
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    if (typeof interceptor.runWhen === 'function' && interceptor.runWhen(config) === false) {
      return;
    }

    synchronousRequestInterceptors = synchronousRequestInterceptors && interceptor.synchronous;

    requestInterceptorChain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  // 响应拦截 队列
  var responseInterceptorChain = [];
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    responseInterceptorChain.push(interceptor.fulfilled, interceptor.rejected);
  });

  var promise;

  if (!synchronousRequestInterceptors) {
    var chain = [dispatchRequest, undefined];

    // 编排整个请求的任务队列
    // [...requestInterceptors, dispatchRequest, undefined, ...responseInterceptors]
    // 使用 Array.prototype.unshift 将 requestInterceptorChain 放在 chain 的前面
    Array.prototype.unshift.apply(chain, requestInterceptorChain);
    chain = chain.concat(responseInterceptorChain);

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

    return promise;
  }


  var newConfig = config;
  while (requestInterceptorChain.length) {
    var onFulfilled = requestInterceptorChain.shift();
    var onRejected = requestInterceptorChain.shift();
    try {
      newConfig = onFulfilled(newConfig);
    } catch (error) {
      onRejected(error);
      break;
    }
  }

  try {
    promise = dispatchRequest(newConfig);
  } catch (error) {
    return Promise.reject(error);
  }

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

  return promise;
};

// 设置别名 axios.get axios.post ...
// Provide aliases for supported request methods
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: (config || {}).data
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

# 派发请求 dispatchRequest

  • lib/core/dispatchRequest.js
  1. 转换请求数据
  2. 定义适配器
  3. 转换响应数据
  4. 返回 response 和 reject 分别对应 then 和 catch
/**
 * Dispatch a request to the server using the configured adapter.
 *
 * @param {object} config The config that is to be used for the request
 * @returns {Promise} The Promise to be fulfilled
 */
module.exports = function dispatchRequest(config) {
  throwIfCancellationRequested(config);

  // Ensure headers exist
  config.headers = config.headers || {};

  // Transform request data
  // 转换请求数据
  config.data = transformData.call(
    config,
    config.data,
    config.headers,
    config.transformRequest
  );

  // Flatten headers
  config.headers = utils.merge(
    config.headers.common || {},
    config.headers[config.method] || {},
    config.headers
  );

  utils.forEach(
    ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
    function cleanHeaderConfig(method) {
      delete config.headers[method];
    }
  );

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

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

    // Transform response data
    response.data = transformData.call(
      config,
      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.call(
          config,
          reason.response.data,
          reason.response.headers,
          config.transformResponse
        );
      }
    }

    return Promise.reject(reason);
  });
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

# 转换 请求 响应数据

transformRequest: [function transformRequest(data, headers) {
  normalizeHeaderName(headers, 'Accept');
  normalizeHeaderName(headers, 'Content-Type');

  // 判断 data 类型 
  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();
  }

  // 设置 Content-Type 类型
  if (utils.isObject(data) || (headers && headers['Content-Type'] === 'application/json')) {
    setContentTypeIfUnset(headers, 'application/json');
    // 将 data 转为 json 字符串返回
    return stringifySafely(data);
  }
  return data;
}],

transformResponse: [function transformResponse(data) {
  var transitional = this.transitional || defaults.transitional;
  var silentJSONParsing = transitional && transitional.silentJSONParsing;
  var forcedJSONParsing = transitional && transitional.forcedJSONParsing;
  var strictJSONParsing = !silentJSONParsing && this.responseType === 'json';

  if (strictJSONParsing || (forcedJSONParsing && utils.isString(data) && data.length)) {
    try {
      // 将 data 转为 JSON 对象返回
      return JSON.parse(data);
    } catch (e) {
      if (strictJSONParsing) {
        if (e.name === 'SyntaxError') {
          throw enhanceError(e, this, 'E_JSON_PARSE');
        }
        throw e;
      }
    }
  }

  return data;
}],
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

# 适配器 adapter

  • 支持 浏览器和Node环境
// lib/core/dispatchRequest.js
var adapter = config.adapter || defaults.adapter;

// lib/defaults.js
adapter: getDefaultAdapter()

function getDefaultAdapter() {
  var adapter;
  if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  }
  return adapter;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// lib/adapters/xhr.js
module.exports = function xhrAdapter(config) {
  return new Promise(function dispatchXhrRequest(resolve, reject) {
    // ...
    var request = new XMLHttpRequest();
    // 设置完整请求路径
    var fullPath = buildFullPath(config.baseURL, config.url);
    request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true) ;
    // 请求超时
    request.timeout = config.timeout;
    request.ontimeout = function handleTimeout() {...}
    // 请求中断
    request.onabort = function handleAbort() {...}
    // ...
    request.send(requestData);
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17