axios 第一步 MergeConfig
axios 源码解析 源码地址
https://github.com/axios/axios
1. 简单使用
1. axios.get("xxx", function(req,res) => { } )
2. axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred'} });
2. 源码初始
在 源码
dist/axios文件 当你调用axios.get()的时候
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
Axios.prototype[method] = function(url, config) {
return this.request(mergeConfig(config || {}, {
method: method,
url: url,
data: (config || {}).data
}));
};
});
当你使用 post 方法 时,如果 是
postForm方法,最终还是调用了 this.request 和 mergeConfig 方法
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
function generateHTTPMethod(isForm) {
// 使用高阶函数,存储 isForm
return function httpMethod(url, data, config) {
return this.request(mergeConfig(config || {}, {
method: method,
headers: isForm ? {
'Content-Type': 'multipart/form-data'
} : {},
url: url,
data: data
}));
};
}
// 等于 Axios.prototype[method] = httpMethod
Axios.prototype[method] = generateHTTPMethod();
Axios.prototype[method + 'Form'] = generateHTTPMethod(true);
});
从 里往外扒开 代码,先从 mergeConfig 开始
源码初始-mergeConfig
mergeConfig顾名思义,就是为了 合并 默认的 config 与 用户传进来的 配置
axios.get("xxx", function(req,res) => { } )
axios({ method: 'post', url: '/user/12345', data: { firstName: 'Fred'} });
mergeConfig(config || {}, {
method: method,
url: url,
data: (config || {}).data
})
先以 axios.get 为例,看 mergeConfig 的处理方法
// 可以看出 config1 是用户的配置,config2 是默认配置
var mergeConfig = function mergeConfig(config1, config2) {
config2 = config2 || {};
var config = {};
utils.forEach(
Object.keys(config1).concat(Object.keys(config2)
), function computeConfigValue(prop) {
// prop = config1 和 config2 的 key 值
// 调用 mergeMap[prop] || mergeDeepProperties 返回一个函数
var merge = mergeMap[prop] || mergeDeepProperties;
var configValue = merge(prop);
(utils.isUndefined(configValue) && merge !== mergeDirectKeys)
|| (config[prop] = configValue);
});
return config;
}
// 先看 mergeMap 就是一个 映射表
var mergeMap = {
'url': valueFromConfig2,
'method': valueFromConfig2,
'data': valueFromConfig2,
'timeout': defaultToConfig2,
'timeoutMessage': defaultToConfig2,
'baseURL': defaultToConfig2,
'cancelToken': defaultToConfig2,
...
}
url, method, data 是 valueFromConfig2
baseURL,timeout,cancelToken 是 defaultToConfig2,
mergeConfig-defaultToConfig2
也就是简单的合并 value 值
function defaultToConfig2(prop) {
if (!utils.isUndefined(config2[prop])) {
return getMergedValue(undefined, config2[prop]);
} else if (!utils.isUndefined(config1[prop])) {
return getMergedValue(undefined, config1[prop]);
}
}
mergeConfig-valueFromConfig2
如果 config2 -{url,method,data} 中有这个 prop ,执行 getMergedValue
function valueFromConfig2(prop) {
if (!utils.isUndefined(config2[prop])) {
return getMergedValue(undefined, config2[prop]);
}
}
getMergedValue
mergeConfig-mergeDeepProperties
// 比较简单,返回的是合并的 value 值
function mergeDeepProperties(prop) {
if (!utils.isUndefined(config2[prop])) {
return getMergedValue(config1[prop], config2[prop]);
} else if (!utils.isUndefined(config1[prop])) {
return getMergedValue(undefined, config1[prop]);
}
}
mergeConfig-getMergedValue
// 如果 target 和 source 是 普通的对象{},执行合并
// 如果 source 是 对象,返回 source
// 如果 source 是 数组,返回 []
function getMergedValue(target, source) {
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;
}
其中用 工具方法 `isPlainObject` 和 `merge`
综上所述 mergeConfig 就是合并 用户配置与 默认配置
axios.get("xxx",{ data:{a:1},f:1 })经过 mergeConfig结果是{ method:"get",url:"xxx",data:{a:1},f:1 }
工具方法
1. isPlainObject
function isPlainObject(val) {
if (kindOf(val) !== 'object') {
return false;
}
var prototype = Object.getPrototypeOf(val);
return prototype === null || prototype === Object.prototype;
}
Object.getPrototypeOf({}) === Object.prototype // true
Object.getPrototypeOf([]) === Object.prototype // false
Object.getPrototypeOf(new Array) === Object.prototype // false
Object.getPrototypeOf(new Date) === Object.prototype // false
2. kindOf
通过一个函数自调用的方式进行返回一个函数,巧妙的使用了闭包,没用使用公共变量,减少了内存泄漏的风险
var toString = Object.prototype.toString;
var kindOf = (function(cache) {
return function(thing) {
var str = toString.call(thing);
return cache[str] || (cache[str] = str.slice(8, -1).toLowerCase());
};
})(Object.create(null));
3. merge
不限制参数
function merge(/* obj1, obj2, obj3, ... */) {
var result = {};
//
function assignValue(val, key) {
// 在这个函数调用中,isPlainObject(result[key]) 和 isPlainObject(val)
// let r = merge({a:1,b:{g:6}},{a:2,b:{f:7}})
// result[key] 是 {g:6}
// val 是 {f:7}
// 第一次遍历 result[key] 是 undefined,执行下面的判断, result[key] = {b:g:6}
// 等到第二次遍历到 下个对象 的时候,发现 result 有这个key 值,进行合并
if (isPlainObject(result[key]) && isPlainObject(val)) {
result[key] = merge(result[key], val);
} else if (isPlainObject(val)) {
// 做了一次深拷贝
result[key] = merge({}, val); // {b:f:7}
// result[key] = val // {b:f:8}
} else if (isArray(val)) {
result[key] = val.slice();
} else {
result[key] = val;
}
}
for (var i = 0, l = arguments.length; i < l; i++) {
forEach(arguments[i], assignValue);
}
return result;
}
let d = {f:7};
let r = merge({a:1,b:d},{a:2,b:d})
d.f = 8
console.log(r)
4. forEach 方法
使用设配器设计模式,可以对 数组 / 对象执行同样的遍历操作
/*
* @param {Object|Array} obj The object to iterate
* @param {Function} fn The callback to invoke for each item
*/
function forEach(obj, fn) {
// Don't bother if no value provided
if (obj === null || typeof obj === 'undefined') {
return;
}
// Force an array if not already something iterable
if (typeof obj !== 'object') {
obj = [obj];
}
if (isArray(obj)) {
// Iterate over array values
for (var i = 0, l = obj.length; i < l; i++) {
fn.call(null, obj[i], i, obj);
}
} else {
// Iterate over object keys
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
fn.call(null, obj[key], key, obj);
}
}
}
}
function isArray(val) {
return Array.isArray(val);
}
今天先写到 mergeConfig 这里,可以看出,axios 有很多 工具方法 用了很多巧思,比如 使用 forEach对数组和对象进行统一的遍历,使用merge进行不限制参数的合并,对深度合并也做了处理,kindOf中使用闭包来对数据进行一次缓存,isPlainObject 利用了 Object.getPrototypeOf(val)===Object.prototype;通过原型链来判断是否是一个普通的对象
接下来是 axios 的核心方法request
time:2022/9/28 星期三