axios 源码笔记(四)

lib/core

Axios.js

  1. axios 实例,接收自定义或者默认的config,并且设置了拦截器属性,分别定义了request和response的拦截器
function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}
  1. 设置原型方法,使每个实例都是得到
Axios.prototype.request = function request(config) {
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  // 第一个值为string,说明应该是url
  if (typeof config === 'string') {
    config = arguments[1] || {};
    config.url = arguments[0];
  } else {
    config = 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';
  }

  // Hook up interceptors middleware
  // 这里维护了一个调用栈,中间为dispatchRequest,和undefined
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);

  // 遍历request拦截器,将每个拦截器的resolve,reject添加在调用栈chain的前面
  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });
  
  // 遍历response拦截器,将每个拦截器的resolve,reject添加在调用栈chain的前面
  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  // 遍历调用栈,成双成的给promise增加then,每个then添加resolve和reject方法
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  // 最终返回的promise上,前面是request拦截器,中间是请求,后面是response拦截器
  return promise;
};
  1. 合并自定义config和默认config,并且返回拼接好params和query的url
Axios.prototype.getUri = function getUri(config) {
  config = mergeConfig(this.defaults, config);
  return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
};
  1. 分别给Axios添加原型方法,并处理data
// 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
    }));
  };
});

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
    }));
  };
});

buildFullPath.js

module.exports = function buildFullPath(baseURL, requestedURL) {
  if (baseURL && !isAbsoluteURL(requestedURL)) {
    return combineURLs(baseURL, requestedURL);
  }
  return requestedURL;
};

如果存在baseUrl并且是相对路径,返回拼接好的url,否则返回原始url

createError.js

封装了error,使返回的error统一化

module.exports = function createError(message, config, code, request, response) {
  var error = new Error(message);
  return enhanceError(error, config, code, request, response);
};

dispatchRequest.js

/**
 * Throws a `Cancel` if cancellation has been requested.
 */
function throwIfCancellationRequested(config) {
  if (config.cancelToken) {
    config.cancelToken.throwIfRequested();
  }
}

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

    return Promise.reject(reason);
  });
};
  1. 当用户定义cancelToken时,会把cancel方法抛给用户,当用户触发cancel时,会通过new cancel生成reason,如果存在reason,直接throw。
  2. transformRequest,这块处理data和header,需要用户自定义方法,在请求发送之前处理data和header
  3. 处理header,将三部分header合并,第一,可以设置全局默认的header,也就是header.common,第二,每个method都设置了对应的content-type等参数,此时找到设置的config,method对应的header,第三用户设置的header
  4. 在上一步找到method对应的header,因此删除不必要的header.method
  5. 返回一个promise,调用adapter,成功和失败,都会组装resposeData,同样支持传入transformResponse来修改返回的data,成功直接返回response,失败reject

InterceptorManager

function InterceptorManager() {
  this.handlers = [];
}

// 首先将拦截器的成功和失败,push到handlers,此时会返回当前拦截器的id,方便后面删除
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};

// 删除对应的拦截器,这块没有直接删除拦截器,只是把对应的拦截器id设置为空,是为了防止拦截器的id发生变化
InterceptorManager.prototype.eject = function eject(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
};

// 遍历拦截器数组,执行传入的方法,这里对应上面的chain执行栈,按顺序插入chain,最终会将request和response各种拦截器按顺序执行
InterceptorManager.prototype.forEach = function forEach(fn) {
  utils.forEach(this.handlers, function forEachHandler(h) {
    if (h !== null) {
      fn(h);
    }
  });
};

module.exports = InterceptorManager;

mergeConfig

module.exports = function mergeConfig(config1, config2) {
  // eslint-disable-next-line no-param-reassign
  config2 = config2 || {};
  var config = {};

  var valueFromConfig2Keys = ['url', 'method', 'data'];
  var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy', 'params'];
  var defaultToConfig2Keys = [
    'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer',
    'timeout', 'timeoutMessage', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',
    'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'decompress',
    'maxContentLength', 'maxBodyLength', 'maxRedirects', 'transport', 'httpAgent',
    'httpsAgent', 'cancelToken', 'socketPath', 'responseEncoding'
  ];
  var directMergeKeys = ['validateStatus'];

  function getMergedValue(target, source) {
  	// 判断是否为纯粹的对象,首先会验证[object Object],其次验证prototype是否为Object本身的prototype
  	// source为config2,如果两个都为存粹对象,那么合并
  	// utils 通过递归实现了深拷贝
  	// 深浅拷贝区别:浅拷贝只复制指向某个对象的指针而不复制对象本身,新旧对象还是共享同一块内存
  	// 深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象
    if (utils.isPlainObject(target) && utils.isPlainObject(source)) {
      return utils.merge(target, source);
    } else if (utils.isPlainObject(source)) {
      return utils.merge({}, source);
    } else if (utils.isArray(source)) {
   	  // 浅复制了原数组中的元素的一个新数组
      return source.slice();
    }
    return source;
  }

  // 此处getMergedValue和上面的区别是,上面不关注config1的属性,只要config2有就复制,mergeDeepProperties则是若不存在config2,会复制config1
  function mergeDeepProperties(prop) {
    if (!utils.isUndefined(config2[prop])) {
      config[prop] = getMergedValue(config1[prop], config2[prop]);
    } else if (!utils.isUndefined(config1[prop])) {
      config[prop] = getMergedValue(undefined, config1[prop]);
    }
  }

  utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {
    if (!utils.isUndefined(config2[prop])) {
      config[prop] = getMergedValue(undefined, config2[prop]);
    }
  });

  utils.forEach(mergeDeepPropertiesKeys, mergeDeepProperties);

  utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {
    if (!utils.isUndefined(config2[prop])) {
      config[prop] = getMergedValue(undefined, config2[prop]);
    } else if (!utils.isUndefined(config1[prop])) {
      config[prop] = getMergedValue(undefined, config1[prop]);
    }
  });
  // 这里使用in,只要对象上包含该key,就返回true(自身属性和prototype上)
  // hasOwnProperty 对象自身属性上包含则返回true
  // 判断对象那些属性为原型中的属性,!Object.hasOwnProperty && in
  utils.forEach(directMergeKeys, function merge(prop) {
    if (prop in config2) {
      config[prop] = getMergedValue(config1[prop], config2[prop]);
    } else if (prop in config1) {
      config[prop] = getMergedValue(undefined, config1[prop]);
    }
  });

  var axiosKeys = valueFromConfig2Keys
    .concat(mergeDeepPropertiesKeys)
    .concat(defaultToConfig2Keys)
    .concat(directMergeKeys);

  var otherKeys = Object
    .keys(config1)
    .concat(Object.keys(config2))
    .filter(function filterAxiosKeys(key) {
      return axiosKeys.indexOf(key) === -1;
    });

  utils.forEach(otherKeys, mergeDeepProperties);

  return config;
};
  1. 合并用户自定义的参数:url,method,data,如果config2中不是undefined,则使用config2的内容,这些多数为必传字段,因此以config2为主
  2. 合并mergeDeepPropertiesKeys,这些参数都是可选,因此如果用户没有手动设置,取默认值,也就是为什么要判断是否存在config1[props],如果存在config2,同时会合并config1和config2
  3. 合并defaultToConfig2Keys,这些参数是可选的,是axios已有默认值的配置项,和上面参数的区别在于这一类参数用于控制请求过程,包括变量和函数,这些是可选的,如果用户设置,以用户设置为主,否则采用默认的参数
  4. 处理剩余的字段,剔除axiosKey中包含的,复制剩余的字段

transformData

module.exports = function transformData(data, headers, fns) {
  /*eslint no-param-reassign:0*/
  utils.forEach(fns, function transform(fn) {
    data = fn(data, headers);
  });

  return data;
};

fns可以是一个函数也可以是一个函数数组,data和headers将执行每个传入的fn

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值