【axios三部曲】二、核心源码解析
目录结构
我们主要看lib里面的源码:
- adapter 适配器,用于处理浏览器或者node环境的适配
- cancel 取消请求
- core 核心源码
- helpers 一些工具帮助函数
- axios.js 入口文件
- defaults.js 默认配置文件
- utils.js 工具函数
axios.js入口文件
我们从入口文件开始
'use strict';
// axios 入口文件
var utils = require('./utils'); // 工具函数
var bind = require('./helpers/bind'); // 绑定函数
var Axios = require('./core/Axios'); // 核心Axios
var mergeConfig = require('./core/mergeConfig'); // 合并对象函数
var defaults = require('./defaults'); // 默认配置
/**
* 用于创建一个Axios的实例对象,Axios实际上是一个构造函数
* Create an instance of Axios
* @param {Object} defaultConfig The default config for the instance
* @return {Axios} A new instance of Axios
*/
function createInstance(defaultConfig) {
// 创建一个Axios的实例对象,但是我们需要的并不是它,我们要的是它原型上的request方法
// 这里把这个context当作上下文过渡
// 如果不清楚Axios具体是什么,可以先看文章后面的解析
var context = new Axios(defaultConfig);
// 重点:绑定Axios原型上的request方法,并指定this指向
// axios(),实际上是一个函数,就是Axios.prototype.request
var instance = bind(Axios.prototype.request, context);
// axios光是一个函数还不够,还需要这样使用:axios.get(url[,config]);
// 于是我们就在这个函数身上添加Axios原型上有的prototype方法进来
// get,delete,head,options,post,put,patch...
utils.extend(instance, Axios.prototype, context);
// 还少了一些Axios原有的属性,也添加进来(defaults,interceptors)
utils.extend(instance, context);
return instance;
}
var axios = createInstance(defaults);
axios.Axios = Axios;
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
// Expose Cancel & CancelToken
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
// Expose all/spread
axios.all = function all(promises) {
return Promise.all(promises);
};
axios.spread = require('./helpers/spread');
module.exports = axios;
// Allow use of default import syntax in TypeScript
module.exports.default = axios;
这里有一些工具函数,对于它的作用还不是很清楚,我们就来仔细看一下:
- bind
- utils.extend
- ...
axios常用的工具函数
这些工具函数可以结合源码上下文来看,效果更好
bind(fn, thisArg)
// 绑定函数的this指向
function bind(fn, thisArg){
return function wrap(){
// 把多个参数传入数组
let args = new Array(arguments.length);
for(let i=0;i<args.length;i++){
args[i] = arguments[i];
}
return fn.apply(thisArg, args);
}
}
forEach(obj, fn)
// 遍历一个Array或者一个Object
function forEach(obj, fn){
if(obj === null || typeof obj === 'undefined'){
return;
}
// 如果不是object,放到数组中
if(typeof obj !== 'object'){
obj = [obj]
}
if(Array.isArray(obj)){
for(let i=0;i<obj.length;i++){
// 回调函数,this指向为null
fn.call(null, obj[i], i, obj);
}
}else{
for(let key in obj){
// 这里需要判断是否是自己身上的属性,不然会循环到prototype上去
if(Object.prototype.hasOwnProperty(obj, key)){
fn.call(null, obj[key], key, obj);
}
}
}
}
extend(obj1, obj2, thisArg)
// 把obj2的属性和方法扩展到obj1
function extend(obj1, obj2, thisArg){
// 遍历obj2
forEach(obj2, function(val, key){
// 如果有thisArg参数,并且val值为函数
if(thisArg && typeof val === 'function'){
// 需要绑定this指向
obj1[key] = bind(thisArg, val);
}else{
obj1[key] = val;
}
});
return obj1;
}
merge()
// 合并对象函数,优先级从右往左,也就是后面的参数优先级越高
function merge(/* obj1, obj2, obj3, ... */){
let result = {};
function assignValue(val, key){
// b->a
// a对象的一个key对应的值是object,并且b对象的值也是一个对象
// 递归调用
if(typeof result[key] === 'object' && typeof val === 'object'){
result[key] = merge(result[key], val);
}else if(typeof val === 'object'){
result[key] = merge({}, val);
}else{
result[key] = val;
}
}
for(let i=0;i<arguments.length;i++){
forEach(arguments[i], assignValue);
}
return result;
}
Axios核心函数
core/Axios.js
'use strict';
//Axios 构造函数
var utils = require('./../utils');
var buildURL = require('../helpers/buildURL');
var InterceptorManager = require('./InterceptorManager'); // 拦截器
var dispatchRequest = require('./dispatchRequest'); // 请求派发
var mergeConfig = require('./mergeConfig'); // 合并config函数
/**
* Create a new instance of Axios
* @param {Object} instanceConfig The default config for the instance
*/
function Axios(instanceConfig) {
// 实例上的默认配置
this.defaults = instanceConfig;
// 实例上的拦截器
this.interceptors = {
request: new InterceptorManager(),
response: new InterceptorManager()
};
}
/**
* 核心方法request
* Dispatch a request
* @param {Object} config The config specific for this request (merged with this.defaults)
*/
Axios.prototype.request = function request(config) {
/*eslint no-param-reassign:0*/
// Allow for axios('example/url'[, config]) a la fetch API
/**
* axios('http://www.baidu.com', {header:{}})
*/
// 对传入的参数做处理,支持axios(url,[,config]) 这中传参写法
if (typeof config === 'string') {
config = arguments[1] || {};
config.url = arguments[0];
} else {
config = config || {};
}
// 合并默认的参数和传入的参数
config = mergeConfig(this.defaults, config);
// Set config.method
// 指定默认的请求方式get
if (config.method) {
config.method = config.method.toLowerCase();
} else if (this.defaults.method) {
config.method = this.defaults.method.toLowerCase();
} else {
config.method = 'get';
}
// Hook up interceptors middleware
// 请求链,一个数组来维护发出真实请求前后要处理的事
// 最终chain会长这样:
// [请求拦截器1,请求拦截器2,..., dispatchRequest, undefined, 响应拦截器1,响应拦截器2...]
var chain = [dispatchRequest, undefined]; // 派发请求,undefined占位用的
var promise = Promise.resolve(config);// promise 成功的Promise,带有config返回到下一个promise
// 请求拦截器添加到数组的头,因为是请求拦截器嘛
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
// 响应拦截器添加到数组的尾,最终返回的结果就是它,依然是promise
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
// chain数组把要处理的事件方法排好了,接下来就是依次执行了
while (chain.length) {
// 数组执行一个,就弹出数组
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
// 获取uri地址
Axios.prototype.getUri = function getUri(config) {
config = mergeConfig(this.defaults, config);
return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\?/, '');
};
// 别名方法delete, get, head, options, 实际上也是调用request
// Provide aliases for supported request methods axios.get axios.post axios.put
utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function (url, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url
}));
};
});
// 与上面方法不同的是,需要传data
utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
/*eslint func-names:0*/
Axios.prototype[method] = function (url, data, config) {
return this.request(utils.merge(config || {}, {
method: method,
url: url,
data: data
}));
};
});
module.exports = Axios;
InterceptorManager 拦截器
我们来看看拦截器做了些什么
core/InterceptorManager.js
'use strict';
var utils = require('./../utils');
// 拦截器的构造函数
function InterceptorManager() {
// 实例属性handlers数组
this.handlers = [];
}
/**
* 把一个新的拦截器添加到这个实例的handlers数组上
* 参数(fulfilled, rejected)其实是一个成功promise函数和一个失败的promise函数
* 返回一个id,用作移除
* Add a new interceptor to the stack
* @param {Function} fulfilled The function to handle `then` for a `Promise`
* @param {Function} rejected The function to handle `reject` for a `Promise`
*
* @return {Number} An ID used to remove interceptor later
*/
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
this.handlers.push({
fulfilled: fulfilled,
rejected: rejected
});
return this.handlers.length - 1;
};
/**
* 通过id移除拦截器
* Remove an interceptor from the stack
* @param {Number} id The ID that was returned by `use`
*/
InterceptorManager.prototype.eject = function eject(id) {
if (this.handlers[id]) {
this.handlers[id] = null;
}
};
/**
* 迭代所有注册的拦截器
* Iterate over all the registered interceptors
* This method is particularly useful for skipping over any
* interceptors that may have become `null` calling `eject`.
*
* @param {Function} fn The function to call for each interceptor
*/
InterceptorManager.prototype.forEach = function forEach(fn) {
utils.forEach(this.handlers, function forEachHandler(h) {
if (h !== null) {
fn(h);
}
});
};
module.exports = InterceptorManager;
dispatchRequest 请求派发
到目前为止,我们并没有真实的发送一个请求,dispatchRequest也并不发送请求,它会去调用适配器(adapter)来做这件事(发送请求)
core/dispatchRequest.js
'use strict';
//发送请求的函数文件
var utils = require('./../utils');
var transformData = require('./transformData'); // 转换数据
var isCancel = require('../cancel/isCancel'); // 取消函数
var defaults = require('../defaults');
/**
* 如果config配置里面有cancelToken配置,就抛一个异常
* Throws a `Cancel` if cancellation has been requested.
*/
function throwIfCancellationRequested(config) {
if (config.cancelToken) {
config.cancelToken.throwIfRequested();
}
}
/**
* 派发一个请求 使用config里面的适配器
* Dispatch a request to the server using the configured adapter.
* @param {object} config The config that is to be used for the request
* @returns {Promise} The Promise to be fulfilled
*/
module.exports = function dispatchRequest(config) {
// 如果配置了取消请求
throwIfCancellationRequested(config);
// Ensure headers exist
// 确保headers头存在
config.headers = config.headers || {};
// 转换请求data数据
config.data = transformData(
config.data,
config.headers,
config.transformRequest
);
// 扁平化,合并头
config.headers = utils.merge(
config.headers.common || {},
config.headers[config.method] || {},
config.headers
);
// 移除头里面的这些方法,
// 既然要开始一个请求了,请求的配置config还是要做一些清理工作
utils.forEach(
['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
function cleanHeaderConfig(method) {
delete config.headers[method];
}
);
// 适配器,如果config里面设置了优先使用,否则用默认的
var adapter = config.adapter || defaults.adapter;
// 适配器返回一个promise,这是一个真正的请求
return adapter(config).then(function onAdapterResolution(response) {
throwIfCancellationRequested(config);
// Transform response data
response.data = transformData(
response.data,
response.headers,
config.transformResponse
);
return response;
}, function onAdapterRejection(reason) {
if (!isCancel(reason)) {
throwIfCancellationRequested(config);
// Transform response data
if (reason && reason.response) {
reason.response.data = transformData(
reason.response.data,
reason.response.headers,
config.transformResponse
);
}
}
return Promise.reject(reason);
});
};
mergeConfig合并对象
core/mergeConfig.js
'use strict';
var utils = require('../utils');
/**
* 合并两个对象,返回一个新对象
* Config-specific merge-function which creates a new config-object
* by merging two configuration objects together.
* @param {Object} config1
* @param {Object} config2
* @returns {Object} New object resulting from merging config2 to config1
*/
module.exports = function mergeConfig(config1, config2) {
// eslint-disable-next-line no-param-reassign
config2 = config2 || {};
var config = {};
// 哪些值来自config2上的key
var valueFromConfig2Keys = ['url', 'method', 'params', 'data'];
// 需要深拷贝的值
var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy'];
// config2的默认key
var defaultToConfig2Keys = [
'baseURL', 'url', 'transformRequest', 'transformResponse', 'paramsSerializer',
'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',
'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress',
'maxContentLength', 'validateStatus', 'maxRedirects', 'httpAgent',
'httpsAgent', 'cancelToken', 'socketPath'
];
// 这些值来自config2上
utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {
if (typeof config2[prop] !== 'undefined') {
config[prop] = config2[prop];
}
});
// 优先使用config2上的,如果值是对象就深拷贝
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];
}
});
// 设置默认值,优先使用config2上的
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];
}
});
// 把所有的key合并到一个数组
var axiosKeys = valueFromConfig2Keys
.concat(mergeDeepPropertiesKeys)
.concat(defaultToConfig2Keys);
// 如果有axiosKeys以外的其他key,相当于用户自定义头
// 这里用了set,数组去重
var otherKeys = Array.from(
new Set(Object.keys(config1).concat(Object.keys(config2)))
).filter((key) => {
return axiosKeys.indexOf(key) === -1;
});
// 遍历其他keys,config2的优先级依然高于config1
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;
};
adapter适配器
这个适配器的作用是:判断是浏览器还是Node环境,
如果是浏览器环境,使用:XMLHttpRequest
如果是Node环境,使用:http,https
具体资料可以参考:XMLHttpRequest,http超文本传输协议
axios执行的流程
分类:
axios
标签:
axios
, javascript
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律