合并用户配置跟默认配置
我们在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";
}