Axios构造函数

Axios构造函数实例化出的axios对象拥有拦截器属性,拦截器属性里有两个拦截器对象,一个是请求拦截器,一个是响应拦截器

下面是结构图

axios 上传参数 传递对象_axios 上传参数 传递对象

然后就是整个Axios最核心的东西,Axios.prototype.request方法,使用axios发请求就是在调用这个方法。

Axios.prototype.request会将请求拦截器,请求,响应拦截器放在一个数组里,形成一个队列,然后按照顺序用Promise.ptotorype.then方法调用,then方法的返回值还是一个promise,因此就形成一个then链,直到队列中所有操作都执行结束,然后将最终的promise返回。这就是Axios的核心实现。

下面是then链的流程图:

axios 上传参数 传递对象_ios_02

下面是Axios.js源代码:

'use strict';

var utils = require('./../utils');
var buildURL = require('../helpers/buildURL');
var InterceptorManager = require('./InterceptorManager');
var dispatchRequest = require('./dispatchRequest');
var mergeConfig = require('./mergeConfig');

/**
 * Create a new instance of Axios
 *
 * @param {Object} instanceConfig The default config for the instance
 */
function Axios(instanceConfig) {
  this.defaults = instanceConfig;//axios实例上的defaults属性,存放默认设置
  this.interceptors = {//axios上的拦截器属性,是一个对象,里面分别存放请求和相应的拦截器对象
    //InterceptorManager对象在添加了拦截器之后,里面的一个拦截器其实是存了拥有两个属性的对象,fulfilled和rejected,分别是promise成功和失败时的回调函数
    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是字符串,说明是请求的url参数
    config = arguments[1] || {};//第二个参数是请求的config
    config.url = arguments[0];//第一个参数赋值给config的url属性
  } else {
    config = config || {};//否则config就是请求配置
  }

  config = mergeConfig(this.defaults, config);
  //调用mergeConfig将默认设置和自定义设置合并,this.defaults上同名属性会覆盖config上的
  config.method = config.method ? config.method.toLowerCase() : 'get';
  //method配置转换成小写字母,如果没传递,默认是get

  // Hook up interceptors middleware
  var chain = [dispatchRequest, undefined];
  //请求和拦截器的执行链
  //执行链数组中的元素都是函数,两个为一组,下面的操作会将两个为一组传递给promise.then()
  //Promise.prototype.then接收两个参数,第一个是resolve,第二个是reject
  //then方法返回的是一个新的Promise实例,因此可以使用链式写法
  var promise = Promise.resolve(config);//初始promise

  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    //遍历this.interceptors.request.handlers数组,对其中的每一个拦截器执行unshiftRequestInterceptors函数
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
    //将请求拦截器插入到执行链的最开头位置
  });

  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    //遍历this.interceptors.response.handlers数组,对其中的每一个拦截器执行pushResponseInterceptors函数
    chain.push(interceptor.fulfilled, interceptor.rejected);
    //将响应拦截器插入到执行链的结尾位置
  });

  while (chain.length) {
    //循环执行链数组,将两个为一组传递给promise.then()
    //将执行链里的请求拦截器和dispatchRequest和响应拦截器按顺序执行完
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;//返回链式promise最终返回的promise对象
};

Axios.prototype.getUri = function getUri(config) {//获取带有查询字符串的url
  config = mergeConfig(this.defaults, config);
  //调用mergeConfig将默认设置和自定义设置合并,this.defaults上同名属性会覆盖config上的
  return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
  //返回带有查询字符串的url,将斜杠转换为空字符串
};

// Provide aliases for supported request methods
//为以下类型请求提供别名调用方法,例如Axios.prototype.delete
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
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(utils.merge(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

module.exports = Axios;

下面是InterceptorManager.js源代码:

'use strict';

var utils = require('./../utils');

function InterceptorManager() {//InterceptorManager构造函数
  this.handlers = [];//存拦截器的数组属性handlers
}

/**
 * Add a new interceptor to the stack
 *
 * @param {Function} fulfilled The function to handle `then` for a `Promise`
 * @param {Function} rejected The function to handle `reject` for a `Promise`
 *
 * @return {Number} An ID used to remove interceptor later
 */
//原型上的use方法,用于添加新的拦截器到InterceptorManager对象的handlers属性里
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  //fulfilled是promise的成功处理函数,rejected是promise的失败处理函数
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;//返回当前拦截器在handlers中的索引,便于之后移除
};

/**
 * Remove an interceptor from the stack
 *
 * @param {Number} id The ID that was returned by `use`
 */
//从handlers中删除拦截器,参数id是use方法返回的handlers数组索引
InterceptorManager.prototype.eject = function eject(id) {
  if (this.handlers[id]) {//如果handlers数组中存在id对应的拦截器,就赋值为null
    this.handlers[id] = null;
  }
};

/**
 * Iterate over all the registered interceptors
 *
 * This method is particularly useful for skipping over any
 * interceptors that may have become `null` calling `eject`.
 *
 * @param {Function} fn The function to call for each interceptor
 */
//遍历所有已注册的拦截器
//这个方法在想要跳过被移除的拦截器的时候非常有用,因为调用eject后对应的拦截器被赋值为null
//参数fn会在遍历的时候调用
InterceptorManager.prototype.forEach = function forEach(fn) {
  utils.forEach(this.handlers, function forEachHandler(h) {
    if (h !== null) {//如果拦截器不为null,传递给fn作为参数来调用
      fn(h);
    }
  });
};

module.exports = InterceptorManager;

buildURL方法,创建一个带有查询字符串参数的url

'use strict';

var utils = require('./../utils');

function encode(val) {//encodeURIComponent转码后将一些特殊字符变回原样
  return encodeURIComponent(val).
    replace(/%40/gi, '@').
    replace(/%3A/gi, ':').
    replace(/%24/g, '$').
    replace(/%2C/gi, ',').
    replace(/%20/g, '+').
    replace(/%5B/gi, '[').
    replace(/%5D/gi, ']');
}

/**
 * Build a URL by appending params to the end
 *
 * @param {string} url The base of the url (e.g., http://)
 * @param {object} [params] The params to be appended
 * @returns {string} The formatted url
 */
//创建一个url,将params参数也就是get请求参数连在原url的后面
module.exports = function buildURL(url, params, paramsSerializer) {
  /*eslint no-param-reassign:0*/
  if (!params) {//如果没有params参数,直接返回原url
    return url;
  }

  var serializedParams;
  if (paramsSerializer) {
    //如果传递了paramsSerializer,就调用将params序列化,结果存入变量serializedParams
    //paramsSerializer是用户传递的自定义函数
    serializedParams = paramsSerializer(params);
  } else if (utils.isURLSearchParams(params)) {
    //如果没有传递paramsSerializer,就判断params是否是URLSearchParams对象
    //如果是,就调用URLSearchParams.toString(),将其变成字符串可以直接使用
    serializedParams = params.toString();
  } else {//既没有传递paramsSerializer也不是原生URLSearchParams对象,执行下面操作
    var parts = [];//key=value形式的字符串

    utils.forEach(params, function serialize(val, key) {
      //调用forEach循环params对象
      if (val === null || typeof val === 'undefined') {
        //如果当前params的值是空,就不做操作
        return;
      }

      if (utils.isArray(val)) {//判断当前params的值是否是一个数组
        key = key + '[]';//如果是数组,key后面连上数组索引的方括号
      } else {
        val = [val];//如果不是数组,当前值等于只有val一个元素的数组
      }

      utils.forEach(val, function parseValue(v) {
        //循环val数组
        if (utils.isDate(v)) {//如果当前值是日期对象
          v = v.toISOString();//将日期对象转换成ISO格式字符串
        } else if (utils.isObject(v)) {//又或者当前值是一个对象
          v = JSON.stringify(v);//将对象字符串化
        }
        parts.push(encode(key) + '=' + encode(v));//parts数组里插入key=value的字符串
      });
    });

    serializedParams = parts.join('&');//将字符串之间用&连成一整个字符串
  }

  if (serializedParams) {
    var hashmarkIndex = url.indexOf('#');//url中hash标记位置
    if (hashmarkIndex !== -1) {//如果含有hash标记就去掉
      url = url.slice(0, hashmarkIndex);
    }

    url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;//将基础url和查询字符串连接起来
  }

  return url;
};