当前位置:   article > 正文

一文深度剖析Axios源码

utils.foreach(['post', 'put', 'patch']

本文概要

  • 前言

  • Axios整体代码结构分析

  • Axios执行流程整体分析

  • Axios执行文件分析

前言

axios 是目前最常用的 http 请求库,可以用于浏览器和 node.js , 在 github 上已有 43k star 左右之多。

Axios 的主要特性包括:

  • 基于 Promise

  • 支持浏览器和 node.js

  • 可添加拦截器和转换请求和响应数据

  • 请求可以取消

  • 自动转换 JSON 数据

  • 客户端支持防范 XSRF

本文 将 带大家一起阅读 axios 的源码, 按照执行流程解析当中的一些封装技巧,分析功能实现。

Axios整体代码结构分析

  1. ├── /dist/ # 项目输出目录
  2. ├── /lib/ # 项目源码目录
  3. │ ├── /cancel/ # 定义取消功能
  4. │ ├── /core/ # 一些核心功能
  5. │ │ ├── Axios.js # axios的核心主类
  6. │ │ ├── dispatchRequest.js # 用来调用http请求适配器方法发送请求
  7. │ │ ├── InterceptorManager.js # 拦截器构造函数
  8. │ │ └── settle.js # 根据http响应状态,改变Promise的状态
  9. │ ├── /helpers/ # 一些辅助方法
  10. │ ├── /adapters/ # 定义请求的适配器 xhr、http
  11. │ │ ├── http.js # 实现http适配器
  12. │ │ └── xhr.js # 实现xhr适配器
  13. │ ├── axios.js # 对外暴露接口
  14. │ ├── defaults.js # 默认配置
  15. │ └── utils.js # 公用工具
  16. ├── package.json # 项目信息
  17. ├── index.d.ts # 配置TypeScript的声明文件
  18. └── index.js # 入口文件

Axios执行流程整体分析

Axios执行文件分析

  • 入口 /lib/axios.js

  1. function createInstance(defaultConfig) {
  2. var context = new Axios(defaultConfig);
  3. // 自定义bind方法,等同于var instance = Axios.prototype.request.bind(context)
  4. // 这句的作用是将request方法指向instance,上下文是context,可以使用instance(option)
  5. // 调用 var instance = bind(Axios.prototype.request, context);
  6. // 根据前面extend方法,使Axios.prototype的方法扩展到instance对象上,这样instance就有了get,post,put等方法
  7. // 并且制定上细纹是context, 这样axios中方法, this执行context
  8. // utils.extend(instance, Axios.prototype, context);
  9. // 将context自身属性和方法扩展到instance上, 因为extend内部使用的forEach方法对对象做for in 遍历时, 只遍历对象本身的属性, 而不会遍历原型链上的属性
  10. // 这样,instance 就有了 defaults、interceptors 属性。(这两个属性后面我们会介绍)
  11. // Copy context to instance
  12. utils.extend(instance, context);
  13. return instance;
  14. }
  15. // 接收默认配置项作为参数(后面会介绍配置项),创建一个Axios实例,最终会被作为对象导出
  16. var axios = createInstance(defaults);

/lib/core/Axios.js是核心包,axios各种方式('delete', 'get', 'head', 'options','post', 'put', 'patch')都是通过request方法发出的。每个axios实例都有一个interceptors实例属性,interceptors对象上有两个属性request、response。InterceptorManager用来实现拦截器的,这个构造函数原型上有3个方法:use、eject、forEach。我们首先看下 InterceptorManager的实现:

  • 拦截器包 /lib/core/InterceptorManager.js

  1. function InterceptorManager() {
  2. this.handlers = []; // 存放拦截器方法,数组内每一项都是有两个属性的对象,两个属性分别对应成功和失败后执行的函数。
  3. }
  4. // 往拦截器里添加拦截方法,使用 unshift,会导致use 后添加的先执行,先添加的后执行,use就是我们在http.js中书写的拦截器
  5. InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  6. this.handlers.push({
  7. fulfilled: fulfilled,
  8. rejected: rejected
  9. });
  10. return this.handlers.length - 1;
  11. };
  12. // 用来注销指定的拦截器
  13. InterceptorManager.prototype.eject = function eject(id) {
  14. if (this.handlers[id]) {
  15. this.handlers[id] = null;
  16. }
  17. };
  18. // 遍历this.handlers,并将this.handlers里的每一项作为参数传给fn执行
  19. InterceptorManager.prototype.forEach = function forEach(fn) {
  20. utils.forEach(this.handlers, function forEachHandler(h) {
  21. if (h !== null) {
  22. fn(h);
  23. }
  24. });
  25. };
  • 核心包  /lib/core/Axios.js

Axios.js主要处理了两个部分,复用请求方法、实现拦截器。 当我们使用 Axios 的实例去发送请求,使用的方法get、post等都是复用了request方法,在request方法中通过 arguments 获取传入的参数,实现了传入参数的重载。 拦截器是axios的一大特色,它的实现原理其实不复杂,核心就是promise的链式调用。

  1. function Axios(instanceConfig) {
  2. this.defaults = instanceConfig;
  3. this.interceptors = {
  4. request: new InterceptorManager(),
  5. response: new InterceptorManager()
  6. };
  7. }
  8. Axios.prototype.request = function request(config) {
  9. /*eslint no-param-reassign:0*/
  10. // Allow for axios('example/url'[, config]) a la fetch API
  11. if (typeof config === 'string') {
  12. config = arguments[1] || {};
  13. config.url = arguments[0];
  14. } else {
  15. config = config || {};
  16. }
  17. config = mergeConfig(this.defaults, config);
  18. config.method = config.method ? config.method.toLowerCase() : 'get';
  19. // Hook up interceptors middleware
  20. // chain 是一个数组
  21. var chain = [dispatchRequest, undefined];
  22. var promise = Promise.resolve(config);
  23. // 使用 use 添加 fulfilled 与 rejected 添加到队列中
  24. // 添加 request 拦截函数的时候使用的是unshift, 这样会导致 use 后添加的先执行,先添加的后执行
  25. this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
  26. chain.unshift(interceptor.fulfilled, interceptor.rejected);
  27. });
  28. // response使用的是push,添加拦截相应函数,这里是先添加先执行,后添加后执行。
  29. this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
  30. chain.push(interceptor.fulfilled, interceptor.rejected);
  31. });
  32. // chain [fulfilled, rejected, ... dispatchRequest, undefined ....,fulfilled, rejected]
  33. // 这里补充一下 fulfilled, rejected 都是肯定是成对出现的, 具体原因可看 InterceptorManager.prototype.use
  34. // promise.then(undefined, undefined) 中当传递的不是function时,会发生值穿。也就是说 use 中可以传入非function,
  35. // 或者传入单个function
  36. while (chain.length) {
  37. promise = promise.then(chain.shift(), chain.shift());
  38. }
  39. return promise;
  40. };
  41. // 复用request 实现了 delete, get, head, options
  42. utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  43. /*eslint func-names:0*/
  44. Axios.prototype[method] = function(url, config) {
  45. return this.request(utils.merge(config || {}, {
  46. method: method,
  47. url: url
  48. }));
  49. };
  50. });
  51. // 复用request 实现了 delete, get, head, options
  52. utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  53. /*eslint func-names:0*/
  54. Axios.prototype[method] = function(url, data, config) {
  55. return this.request(utils.merge(config || {}, {
  56. method: method,
  57. url: url,
  58. data: data
  59. }));
  60. };
  61. });

Axios.prototype.request方法会调用dispatchRequest方法,而dispatchRequest方法会调用xhrAdapter方法,xhrAdapter方法返回的是还一个Promise对象,调用defaults的getDefaultAdapter方法,如果是浏览器端使用xhr,如果是服务端调用的是http。接下来我们一次解析dispatchRequest,xhrAdapter,xhr 文件。

  • /lib/core/dispatchRequest.js

dispatchRequest做了三件事

  • 拿到config对象,对config进行传给http请求适配器前的最后处理

  • http请求适配器根据config配置,发起请求

  • http请求适配器请求完成后,如果成功则根据header、data、和config.transformResponse

  1. module.exports = function dispatchRequest(config) {
  2. throwIfCancellationRequested(config);
  3. // 如果包含baseUrl, 并且不是config.url绝对路径,组合baseUrl以及config.url
  4. if (config.baseURL && !isAbsoluteURL(config.url)) {
  5. // 组合baseURL与url形成完整的请求路径
  6. config.url = combineURLs(config.baseURL, config.url);
  7. }
  8. config.headers = config.headers || {};
  9. // 使用/lib/defaults.js中的transformRequest方法,对config.headers和config.data进行格式化
  10. // 比如将headers中的Accept,Content-Type统一处理成大写
  11. // 比如如果请求正文是一个Object会格式化为JSON字符串,并添加application/json;charset=utf-8的Content-Type
  12. // 等一系列操作
  13. config.data = transformData(
  14. config.data,
  15. config.headers,
  16. config.transformRequest
  17. );
  18. // 合并不同配置的headers,config.headers的配置优先级更高
  19. config.headers = utils.merge(
  20. config.headers.common || {},
  21. config.headers[config.method] || {},
  22. config.headers || {}
  23. );
  24. // 删除headers中的method属性
  25. utils.forEach(
  26. ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
  27. function cleanHeaderConfig(method) {
  28. delete config.headers[method];
  29. }
  30. );
  31. // 如果config配置了adapter,使用config中配置adapter的替代默认的请求方法
  32. var adapter = config.adapter || defaults.adapter;
  33. // 使用adapter方法发起请求(adapter根据浏览器环境或者Node环境会有不同,http请求适配器会优先使用config上自定义的适配器,没有配置时才会使用默认的XHR或http适配器,不过大部分时候,axios提供的默认适配器是能够满足我们的
  34. return adapter(config).then(
  35. // 请求正确返回的回调
  36. function onAdapterResolution(response) {
  37. // 判断是否以及取消了请求,如果取消了请求抛出以取消
  38. throwIfCancellationRequested(config);
  39. // 使用/lib/defaults.js中的transformResponse方法,对服务器返回的数据进行格式化
  40. // 例如,使用JSON.parse对响应正文进行解析
  41. response.data = transformData(
  42. response.data,
  43. response.headers,
  44. config.transformResponse
  45. );
  46. return response;
  47. },
  48. // 请求失败的回调
  49. function onAdapterRejection(reason) {
  50. if (!isCancel(reason)) {
  51. throwIfCancellationRequested(config);
  52. if (reason && reason.response) {
  53. reason.response.data = transformData(
  54. reason.response.data,
  55. reason.response.headers,
  56. config.transformResponse
  57. );
  58. }
  59. }
  60. return Promise.reject(reason);
  61. }
  62. );
  63. };

dispatchRequest方法内,首先得到xhrAdapter方法返回的Promise对象, 然后通过.then方法,对xhrAdapter返回的Promise对象的成功或失败结果再次加工, 成功的话,则将处理后的response返回, 失败的话,则返回一个状态为rejected的Promise对象. 用户调用axios()方法时,就可以直接调用Promise的.then或.catch进行业务处理了。

  1. return adapter(config).then(function onAdapterResolution(response) {
  2. // ...
  3. return response;
  4. }, function onAdapterRejection(reason) {
  5. // ...
  6. return Promise.reject(reason);
  7. });
  8. };

可以看到adapter 若config没有配置adapter使用的是defaults的adapter,我们首先看下defaults中的adapter函数,稍后会详细介绍defaults源码,

  1. function getDefaultAdapter() {
  2. var adapter;
  3. // Only Node.JS has a process variable that is of [[Class]] process
  4. if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
  5. // For node use HTTP adapter
  6. adapter = require('./adapters/http');
  7. } else if (typeof XMLHttpRequest !== 'undefined') {
  8. // For browsers use XHR adapter
  9. adapter = require('./adapters/xhr');
  10. }
  11. return adapter;
  12. }

调用defaults的getDefaultAdapter方法,如果是浏览器端使用xhr,如果是服务端调用的是http。我们接下来看下xhr文件。

  • /lib/adapters/xhr.js

xhr.js导出的xhrAdapter方法是axios在浏览器环境下使用的默认请求方法。我们可以在配置中使用adapter配置项对默认请求方法进行替换。

  1. module.exports = function xhrAdapter(config) {
  2. return new Promise(function dispatchXhrRequest(resolve, reject) {
  3. var requestData = config.data;
  4. var requestHeaders = config.headers;
  5. // 判断是否是FormData对象, 如果是, 删除header的Content-Type字段,让浏览器自动设置Content-Type字段
  6. if (utils.isFormData(requestData)) {
  7. delete requestHeaders['Content-Type'];
  8. }
  9. // 创建xtr对象
  10. var request = new XMLHttpRequest();
  11. // 设置http请求头中的Authorization字段
  12. // 关于Authorization字段
  13. // 更多内容参考https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Authorization
  14. if (config.auth) {
  15. var username = config.auth.username || '';
  16. var password = config.auth.password || '';
  17. // 使用btoa方法base64编码username和password
  18. requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);
  19. }
  20. // 初始化请求方法
  21. // open(method: 请求的http方法, url: 请求的url地址, 是否支持异步)
  22. request.open(
  23. config.method.toUpperCase(),
  24. buildURL(config.url, config.params, config.paramsSerializer),
  25. true
  26. );
  27. // 设置超时时间
  28. request.timeout = config.timeout;
  29. // 监听readyState状态的变化,当readyState状态为4的时候,表示ajax请求成功
  30. request.onreadystatechange = function handleLoad() {
  31. if (!request || request.readyState !== 4) {
  32. return;
  33. }
  34. // request.status响应的数字状态码,在完成请求前数字状态码等于0
  35. // 如果request.status出错返回的也是0,但是file协议除外,status等于0也是一个成功的请求
  36. // 更多内容请参考 https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/status
  37. if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {
  38. return;
  39. }
  40. // getAllResponseHeaders方法会返回所有的响应头
  41. // 更多内容请参考 https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/getAllResponseHeaders
  42. var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;
  43. // 如果没有设置数据响应类型(默认为“json”)或者responseType设置为text时,获取request.responseText值否则是获取request.response
  44. // responseType是一个枚举类型,手动设置返回数据的类型 更多请参考 https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/responseType
  45. // responseText是全部后端的返回数据为纯文本的值 更多请参考 https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/responseText
  46. // response为正文,response的类型取决于responseType 更多请参考 https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/response
  47. var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;
  48. var response = {
  49. data: responseData, // 响应正文
  50. status: request.status, // 响应状态
  51. statusText: request.statusText, // 响应状态的文本信息
  52. headers: responseHeaders, // 响应头
  53. config: config,
  54. request: request
  55. };
  56. // status >= 200 && status < 300 resolve
  57. // 否则reject
  58. settle(resolve, reject, response);
  59. request = null;
  60. };
  61. // ajax中断时触发
  62. request.onabort = function handleAbort() {
  63. if (!request) {
  64. return;
  65. }
  66. // 抛出Request aborted错误
  67. reject(createError('Request aborted', config, 'ECONNABORTED', request));
  68. request = null;
  69. };
  70. // ajax失败时触发
  71. request.onerror = function handleError() {
  72. // 抛出Network Error错误
  73. reject(createError('Network Error', config, null, request));
  74. request = null;
  75. };
  76. // ajax请求超时时调用
  77. request.ontimeout = function handleTimeout() {
  78. // 抛出 timeout错误
  79. reject(createError('timeout of ' + config.timeout + 'ms exceeded', config, 'ECONNABORTED',
  80. request));
  81. request = null;
  82. };
  83. // 判断当前是为标准浏览器环境,如果是,添加xsrf头
  84. // 什么是xsrf header?xsrf header是用来防御CSRF攻击
  85. // 原理是服务端生成一个XSRF-TOKEN,并保存到浏览器的cookie中,在每次请求中ajax都会将XSRF-TOKEN设置到request header中
  86. // 服务器会比较cookie中的XSRF-TOKEN与header中XSRF-TOKEN是否一致
  87. // 根据同源策略,非同源的网站无法读取修改本源的网站cookie,避免了伪造cookie
  88. if (utils.isStandardBrowserEnv()) {
  89. var cookies = require('./../helpers/cookies');
  90. // withCredentials设置跨域请求中是否应该使用cookie 更多请参考 https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest/withCredentials
  91. // (设置了withCredentials为true或者是同源请求)并且设置xsrfCookieName
  92. var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) && config.xsrfCookieName ?
  93. // 读取cookie中XSRF-TOKEN
  94. cookies.read(config.xsrfCookieName) :
  95. undefined;
  96. if (xsrfValue) {
  97. // 在request header中设置XSRF-TOKEN
  98. requestHeaders[config.xsrfHeaderName] = xsrfValue;
  99. }
  100. }
  101. // setRequestHeader是用来设置请求头部的方法
  102. if ('setRequestHeader' in request) {
  103. // 将config中配置的requestHeaders,循环设置到请求头上
  104. utils.forEach(requestHeaders, function setRequestHeader(val, key) {
  105. if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {
  106. delete requestHeaders[key];
  107. } else {
  108. request.setRequestHeader(key, val);
  109. }
  110. });
  111. }
  112. // 设置xhr对象的withCredentials属性,是否允许cookie进行跨域请求
  113. if (config.withCredentials) {
  114. request.withCredentials = true;
  115. }
  116. // 设置xhr对象的responseType属性
  117. if (config.responseType) {
  118. try {
  119. request.responseType = config.responseType;
  120. } catch (e) {
  121. if (config.responseType !== 'json') {
  122. throw e;
  123. }
  124. }
  125. }
  126. // 下载进度
  127. if (typeof config.onDownloadProgress === 'function') {
  128. request.addEventListener('progress', config.onDownloadProgress);
  129. }
  130. // 上传进度
  131. // request.upload XMLHttpRequest.upload 属性返回一个 XMLHttpRequestUpload对象,用来表示上传的进度
  132. if (typeof config.onUploadProgress === 'function' && request.upload) {
  133. request.upload.addEventListener('progress', config.onUploadProgress);
  134. }
  135. if (config.cancelToken) {
  136. // 取消请求,在介绍/lib/cancel/CancelToken.js中以及介绍,这里不在赘述
  137. config.cancelToken.promise.then(function onCanceled(cancel) {
  138. if (!request) {
  139. return;
  140. }
  141. request.abort();
  142. reject(cancel);
  143. request = null;
  144. });
  145. }
  146. if (requestData === undefined) {
  147. requestData = null;
  148. }
  149. // 发送http请求
  150. request.send(requestData);
  151. });
  152. };

xhrAdapter内的XHR发送请求成功后会执行这个Promise对象的resolve方法,并将请求的数据传出去, 反之则执行reject方法,并将错误信息作为参数传出去。接下来走settle.js.

  1. function settle(resolve, reject, response) {
  2. var validateStatus = response.config.validateStatus;
  3. if (!response.status || !validateStatus || validateStatus(response.status)) {
  4. resolve(response);
  5. } else {
  6. reject( /**/ );
  7. }
  8. };

dispatch 中 transformData对数据进行处理的,以及defaults的getDefaultAdapter方法,我们接下来看下/lib/defaults.js。

  • /lib/defaults.js

defaults.js文件中配置了,axios默认的请求头、不同的环境下axios默认使用的请求方法、格式化请求正文的方法,格式化响应正文方法等内容

  1. var utils = require('./utils');
  2. var normalizeHeaderName = require('./helpers/normalizeHeaderName');
  3. // 默认Content-Type
  4. var DEFAULT_CONTENT_TYPE = {
  5. 'Content-Type': 'application/x-www-form-urlencoded'
  6. };
  7. // 设置ContentType,在没有设置的情况下
  8. function setContentTypeIfUnset(headers, value) {
  9. if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {
  10. headers['Content-Type'] = value;
  11. }
  12. }
  13. // 根据当前环境,获取默认的请求方法
  14. function getDefaultAdapter() {
  15. var adapter;
  16. // 判断当前环境是否存在process对象
  17. if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
  18. // node环境
  19. adapter = require('./adapters/http');
  20. } else if (typeof XMLHttpRequest !== 'undefined') {
  21. // 浏览器环境
  22. adapter = require('./adapters/xhr');
  23. }
  24. return adapter;
  25. }
  26. var defaults = {
  27. // 默认的请求方法
  28. adapter: getDefaultAdapter(),
  29. // 格式化请求requestData,这会请求发送前使用,在默认情况下,axios将会自动的将传入的data对象序列化为JSON字符串,
  30. transformRequest: [
  31. function transformRequest(data, headers) {
  32. // 格式化header属性名,将header中不标准的属性名,格式化为Accept属性名
  33. normalizeHeaderName(headers, 'Accept');
  34. // 格式化header属性名,将header中不标准的属性名,格式化为Content-Type属性名
  35. normalizeHeaderName(headers, 'Content-Type');
  36. if (utils.isFormData(data) ||
  37. utils.isArrayBuffer(data) ||
  38. utils.isBuffer(data) ||
  39. utils.isStream(data) ||
  40. utils.isFile(data) ||
  41. utils.isBlob(data)
  42. ) {
  43. return data;
  44. }
  45. if (utils.isArrayBufferView(data)) {
  46. return data.buffer;
  47. }
  48. // URLSearchParams提供了一些用来处理URL查询字符串接口
  49. // 如果是URLSearchParams对象
  50. if (utils.isURLSearchParams(data)) {
  51. // Content-Type设置为application/x-www-form-urlencoded
  52. // application/x-www-form-urlencoded,数据被编码成以&分隔的键值对
  53. setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');
  54. return data.toString();
  55. }
  56. // 如果是对象
  57. if (utils.isObject(data)) {
  58. // Content-Type设置为application/json
  59. setContentTypeIfUnset(headers, 'application/json;charset=utf-8');
  60. // 将请求正文格式化为JSON字符串,并返回
  61. return JSON.stringify(data);
  62. }
  63. return data;
  64. }
  65. ],
  66. // 格式化响应resposeData,这会响应接受后使用,将响应数据中的JSON字符串转换为JavaScript对象
  67. transformResponse: [
  68. function transformResponse(data) {
  69. if (typeof data === 'string') {
  70. try {
  71. data = JSON.parse(data);
  72. } catch (e) { /* Ignore */ }
  73. }
  74. return data;
  75. }
  76. ],
  77. // 默认超时时间
  78. timeout: 0,
  79. // xsrf设置的cookie的key
  80. xsrfCookieName: 'XSRF-TOKEN',
  81. // xsrf设置header的key
  82. xsrfHeaderName: 'X-XSRF-TOKEN',
  83. maxContentLength: -1,
  84. // 验证请求的状态
  85. // 在处理请求的Promise会被使用
  86. validateStatus: function validateStatus(status) {
  87. return status >= 200 && status < 300;
  88. }
  89. };
  90. defaults.headers = {
  91. // 通用的HTTP字段
  92. // Accept告知客户端可以处理的类型
  93. common: {
  94. 'Accept': 'application/json, text/plain, */*'
  95. }
  96. };
  97. utils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {
  98. defaults.headers[method] = {};
  99. });
  100. // 为post,put,patch请求设置默认的Content-Type
  101. utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  102. defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);
  103. });
  104. module.exports = defaults;

默认的defaults配置项里已经自定义了一个请求转换器和一个响应转换器,请求转换器的使用地方是http请求前,使用请求转换器对请求数据做处理, 然后传给http请求适配器使用,在默认情况下,axios将会自动的将传入的data对象序列化为JSON字符串,将响应数据中的JSON字符串转换为JavaScript对象,看下transformData方法的代码, 主要遍历转换器数组,分别执行每一个转换器,根据data和headers参数,返回新的data。响应转换器的使用地方是在http请求完成后,根据http请求适配器的返回值做数据转换处理。

  • /lib/core/transformData.js

  1. function transformData(data, headers, fns) {
  2. utils.forEach(fns, function transform(fn) {
  3. data = fn(data, headers);
  4. });
  5. return data;
  6. };
  7. // /lib/core/dispatchRequest.js
  8. return adapter(config).then(function onAdapterResolution(response) {
  9. // ...
  10. response.data = transformData(
  11. response.data,
  12. response.headers,
  13. config.transformResponse
  14. );
  15. return response;
  16. }, function onAdapterRejection(reason) {
  17. if (!isCancel(reason)) {
  18. // ...
  19. if (reason && reason.response) {
  20. reason.response.data = transformData(
  21. reason.response.data,
  22. reason.response.headers,
  23. config.transformResponse
  24. );
  25. }
  26. }
  27. return Promise.reject(reason);
  28. });

拦截器同样可以实现转换请求和响应数据的需求,但根据作者的设计和综合代码可以看出, 在请求时,拦截器主要负责修改config配置项,数据转换器主要负责转换请求体,比如转换对象为字符串 在请求响应后,拦截器可以拿到response,数据转换器主要负责处理响应体,比如转换字符串为对象。

axios 的大体流程 如上述般大体介绍完了。如后续有更多思考会出更多续集.

推荐阅读

(点击标题可跳转阅读)

RxJS入门
Javascript条件逻辑设计重构
Promise知识点自测

你不知道的React Diff
你不知道的GIT神操作
程序中代码坏味道(上)

程序中代码坏味道(下)

学习Less,看这篇就够了

从表单抽象到表单中台

vue源码分析(1)- new Vue

实战LeetCode 系列(一) (题目+解析)

一文掌握Javascript函数式编程重点

实战LeetCode - 前端面试必备二叉树算法

一文读懂 React16.0-16.6 新特性(实践 思考)

阿里、网易、滴滴、今日头条、有赞.....等20家面试真题

30分钟学会 snabbdom 源码,实现精简的 Virtual DOM 库


觉得本文对你有帮助?请分享给更多人

关注「React中文社区」加星标,每天进步

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/tf789/article/detail/60810
推荐阅读
相关标签
  

闽ICP备14008679号