【axios源码】config合并的方法

很多框架都涉及到一个configMerge的部分。本文主要分析axios的configMerge部分源代码,主要回答两个问题:axios如何进行configMerge以及它为什么这么做。文章最后会给出相关部分的源代码。

一、预备概念

在源码解读之前,我们先进行一些准备工作。首先我们将config分为两种:UserConfigDefaultConfigUserConfig是创建实例处理化传递的根据应用场景而变化的参数,DefaultConfig是在框架编写时默认的参数。而configMerge的目标就是合并两种config。

二、源码解读

2.1 属性分类

首先axios将它用到的所有config属性分为三类:

  1. 没有初始值,其值必须由初始化的时候指定
  2. 需要合并的属性,处理时需要将Object对象合并
  3. 普通属性,处理时如果UserConfig中有则以UserConfig为准,否则取DefaultConfig的值。这些属性其值的类型除了numberstring甚至可以为functionarray等复杂类型。
  var valueFromConfig2Keys = ['url', 'method', 'params', 'data'];
  var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy'];
  var defaultToConfig2Keys = [
    'baseURL', 'url', 'transformRequest', 'transformResponse', 'paramsSerializer',
    'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',
    'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress',
    'maxContentLength', 'validateStatus', 'maxRedirects', 'httpAgent',
    'httpsAgent', 'cancelToken', 'socketPath'
  ];

2.2 分别处理

对于第一类先判断在UserConfig里面属性对应的值是否存在,如果存在就拿出来。

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

对于第二类属性都是需要合并的object类型,因此使用专门的函数 utils.deepMerge来合并两个object

  utils.forEach(mergeDeepPropertiesKeys, function mergeDeepProperties(prop) {
    if (utils.isObject(config2[prop])) {
      config[prop] = utils.deepMerge(config1[prop], config2[prop]);
    } else if (typeof config2[prop] !== 'undefined') {
      config[prop] = config2[prop];
    } else if (utils.isObject(config1[prop])) {
      config[prop] = utils.deepMerge(config1[prop]);
    } else if (typeof config1[prop] !== 'undefined') {
      config[prop] = config1[prop];
    }
  });

对于第三类,它的处理逻辑和第二类一样。如果userConfig里面存在那么以userConfig为准,否则取默认值,只不过此时是值类型,不需要前面那么麻烦。代码如下:

  utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {
    if (typeof config2[prop] !== 'undefined') {
      config[prop] = config2[prop];
    } else if (typeof config1[prop] !== 'undefined') {
      config[prop] = config1[prop];
    }
  });

2.3 剩余属性补全

获取userconfig中存在但是还没有被添加到config中的属性,一一将其添加到config。

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

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

  utils.forEach(otherKeys, function otherKeysDefaultToConfig2(prop) {
    if (typeof config2[prop] !== 'undefined') {
      config[prop] = config2[prop];
    } else if (typeof config1[prop] !== 'undefined') {
      config[prop] = config1[prop];
    }
  });

三、问题

  1. valueFromConfig2Keys 中的属性不需要单独分出来,但是这些属性具有一些特点,UserConfig中才有,因此将其单独拿出来代码更容易理解。如果将其并入defaultToConfig2Keys中也是可以的,这样仅会缩减5行代码。
  2. 这个合并过程看起来很冗余,在userconfig的基础上对其进行扩展那不是更方便?但是要求UserConfig和DefaultConfig都不变就只能这样做了,这是axios的应用场景导致的。

四、完整源码

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

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

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

  utils.forEach(mergeDeepPropertiesKeys, function mergeDeepProperties(prop) {
    if (utils.isObject(config2[prop])) {
      config[prop] = utils.deepMerge(config1[prop], config2[prop]);
    } else if (typeof config2[prop] !== 'undefined') {
      config[prop] = config2[prop];
    } else if (utils.isObject(config1[prop])) {
      config[prop] = utils.deepMerge(config1[prop]);
    } else if (typeof config1[prop] !== 'undefined') {
      config[prop] = config1[prop];
    }
  });

  utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {
    if (typeof config2[prop] !== 'undefined') {
      config[prop] = config2[prop];
    } else if (typeof config1[prop] !== 'undefined') {
      config[prop] = config1[prop];
    }
  });

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

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

  utils.forEach(otherKeys, function otherKeysDefaultToConfig2(prop) {
    if (typeof config2[prop] !== 'undefined') {
      config[prop] = config2[prop];
    } else if (typeof config1[prop] !== 'undefined') {
      config[prop] = config1[prop];
    }
  });

  return config;
};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Axios是一个基于Promise的HTTP请求库,可以用在浏览器和Node.js中。平时在Vue项目中,经常使用...
    多啦斯基周阅读 882评论 0 0
  • 自己动手实现一个 axios 前言 作为一名前端er,对于数据请求的第三方工具axios,一定不会陌生,如果还是有...
    Cryptic阅读 3,440评论 0 3
  • Axios是近几年非常火的HTTP请求库,官网上介绍Axios 是一个基于 promise 的 HTTP 库,可以...
    milletmi阅读 3,512评论 0 9
  • 1.属性的简洁表示法 允许直接写入变量和函数 上面代码表明,ES6 允许在对象之中,直接写变量。这时,属性名为变量...
    雨飞飞雨阅读 1,147评论 0 3
  • axios 源码解析 以一个简单示例进行说明 上述代码演示了如何发起axios请求,先从require('axio...
    Lhysea阅读 4,732评论 0 1