从零实现axios(2.2小节-合并配置)

131 阅读3分钟

合并用户配置跟默认配置

我们在Axios类中合并用户配置跟默认配置,默认配置的目的是为了能让用户开箱即用,而用户配置比默认配置拥有更高的优先级,是为了能让用户可以根据项目来自定义一些配置项,代码如下

Axios.prototype.request = function request(configOrUrl, config) {
  if (typeof configOrUrl === "string") {
    config = config || {};
    config.url = configOrUrl;
  } else {
    config = config || {};
  }

  // 我们在这里合并配置
  config = mergeConfig(this.defaults, config);

  // 以下部分代码请参考前面的章节或源码
};

我们在 core 文件夹下创建 mergeConfig.js 文件,在里面实现mergeConfig函数,该函数接收两个配置对象参数,第一个参数是默认配置对象,第二个参数是用户自定义配置对象。该函数采取了策略模式来合并配置对象,即不同的 key 会采取不同的合并策略,vue 中也是用这种设计模式来合并配置对象。valueFromConfig2函数定义了哪些属性只能从 config2 对象中取值,而不会从 config1 对象中取值;defaultToConfig2 函数定义了哪些属性默认使用 config2 对象上的值,如果值不存在则使用 config1 对象的值;mergeDeepProperties是默认的策略函数,如果配置对象中存在 mergeMap 中没有的 key,则用该处理函数来处理配置项;mergeDirectKeys函数定义了哪些属性可以直接合并,会直接合并配置项

"use strict";

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

/**
 *  通过合并两个配置对象创建一个新的配置对象
 *
 *
 * @param {Object} config1
 * @param {Object} config2
 * @returns {Object} 新的配置对象
 */
module.exports = function mergeConfig(config1, config2) {
  config2 = config2 || {};
  var config = {};

  // 该函数会根据target和source的值类型,来做出相应的处理,并返回处理后的值
  function getMergedValue(target, source) {
    if (utils.isPlainObject(target) && utils.isPlainObject(source)) {
      // 如果target跟source是纯对象,则把它们合并成一个新的对象
      return utils.merge(target, source);
    } else if (utils.isPlainObject(source)) {
      // 如果只有source对象是一个纯对象,则跟一个空对象合并成新的对象
      return utils.merge({}, source);
    } else if (utils.isArray(source)) {
      // 如果source是数组,则slice出一个新的数组
      return source.slice();
    }
    return source;
  }

  // 默认的策略函数,如果值是纯对象,则会进行深度copy
  function mergeDeepProperties(prop) {
    if (!utils.isUndefined(config2[prop])) {
      // 值config2[prop]存在,则跟config1[prop]合并为新的值
      return getMergedValue(config1[prop], config2[prop]);
    } else if (!utils.isUndefined(config1[prop])) {
      // 值config2[prop]不存在,config1[prop]存在,则单独把config1[prop]合并到config中
      return getMergedValue(undefined, config1[prop]);
    }
  }

  // 该函数只从config2(用户自定义配置对象)中取值
  function valueFromConfig2(prop) {
    if (!utils.isUndefined(config2[prop])) {
      return getMergedValue(undefined, config2[prop]);
    }
  }

  // 如果用户自定义配置对象中有值则用该值,否则取默认对象的值
  function defaultToConfig2(prop) {
    if (!utils.isUndefined(config2[prop])) {
      return getMergedValue(undefined, config2[prop]);
    } else if (!utils.isUndefined(config1[prop])) {
      return getMergedValue(undefined, config1[prop]);
    }
  }

  // 可以直接合并的key, 会直接合并config1,config2的值
  function mergeDirectKeys(prop) {
    if (prop in config2) {
      return getMergedValue(config1[prop], config2[prop]);
    } else if (prop in config1) {
      return getMergedValue(undefined, config1[prop]);
    }
  }

  var mergeMap = {
    url: valueFromConfig2,
    method: valueFromConfig2,
    data: valueFromConfig2,
    baseURL: defaultToConfig2,
    transformRequest: defaultToConfig2,
    transformResponse: defaultToConfig2,
    paramsSerializer: defaultToConfig2,
    timeout: defaultToConfig2,
    timeoutMessage: defaultToConfig2,
    withCredentials: defaultToConfig2,
    adapter: defaultToConfig2,
    responseType: defaultToConfig2,
    xsrfCookieName: defaultToConfig2,
    xsrfHeaderName: defaultToConfig2,
    onUploadProgress: defaultToConfig2,
    onDownloadProgress: defaultToConfig2,
    decompress: defaultToConfig2,
    maxContentLength: defaultToConfig2,
    maxBodyLength: defaultToConfig2,
    beforeRedirect: defaultToConfig2,
    transport: defaultToConfig2,
    httpAgent: defaultToConfig2,
    httpsAgent: defaultToConfig2,
    cancelToken: defaultToConfig2,
    socketPath: defaultToConfig2,
    responseEncoding: defaultToConfig2,
    validateStatus: mergeDirectKeys,
  };

  utils.forEach(
    // 取出所有的key, 并且遍历它们
    Object.keys(config1).concat(Object.keys(config2)),
    function computeConfigValue(prop) {
      // 不同的key,对应不同的策略处理函数
      var merge = mergeMap[prop] || mergeDeepProperties;
      // 获取新的值
      var configValue = merge(prop);
      // 如果该key不可以直接合并,且对应的值不存在,则直接忽略,否则设为新的configValue
      (utils.isUndefined(configValue) && merge !== mergeDirectKeys) ||
        (config[prop] = configValue);
    }
  );

  return config;
};

mergeConfig函数用到了一些 utils.js 中的辅助函数,我们来实现这些函数

// 判断值是不是未定义
function isUndefined(val) {
  return typeof val === "undefined";
}