美文网首页axios源码解析
【axios源码】config合并的方法

【axios源码】config合并的方法

作者: 可以秀但没必要 | 来源:发表于2020-01-21 03:52 被阅读0次

    很多框架都涉及到一个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源码】config合并的方法

        本文链接:https://www.haomeiwen.com/subject/fqvszctx.html