经过了上文的说明,我们已经知道了axios库是什么并且对axios的基础使用也有了一定的了解。
接下来,就开始对axios进行更深层次的学习了,将要去探究axios的原理,它有哪些功能?这些功能是如何实现?具体的工作流程是什么?
学习axios的原理,需要先得到axios的源码内容:
git clone https://github.com/axios/axios.git
将源码clone下来之后,在编辑器中打开,可以看到其中的目录结构,介绍几个主要目录:
lib目录中是axios库的核心代码,包含适配器、请求取消、核心库、辅助函数等文件夹和axios.js、默认配置项、工具类等js文件。
目录结构如下:
lib
└─ adapters
├─ http.js // node 环境下利用 http 模块发起请求
├─ xhr.js // 浏览器环境下利用 xhr 发起请求
└─ cancel
├─ Cancel.js
├─ CancelToken.js
├─ isCancel.js
└─ core
├─ Axios.js // 生成 Axios 实例
├─ InterceptorManager.js // 拦截器
├─ dispatchRequest.js // 调用适配器发起请求
...
└─ helpers
├─ mergeConfig.js // 合并配置
├─ ...
├─ axios.js // 入口文件
├─ defaults.js // axios 默认配置项
├─ utils.js
这里扯一句闲话,我遇到过做前端几年了还不知道从哪里查看项目启动命令和入口文件的,当时很震惊!!!是我还是别人出问题了???
在正常的Web项目中,我是首先去看项目package.json文件,里面的script部分中的指令就和项目的启动和打包有关。而入口文件则是要看webpack打包配置文件。
在axios的代码里,从根目录下的webpack.config.js中可以看到入口文件是同目录下的index.js
var config = {
entry: './index.js',
output: {
path: __dirname + '/dist/',
filename: name + '.js',
sourceMapFilename: name + '.map',
library: 'axios',
libraryTarget: 'umd'
},
}
而在index.js中,又可以看到
module.exports = require('./lib/axios');
所以,我们第一步真正要探究的其实是lib文件夹下的axios.js
在lib文件夹下axios.js最终导出的对象,就是我们所使用axios。
这里主要的内容可分为四部分:
以下就根据这四部分进行学习。
根据导入内容,也分为几大模块:
// 工具函数
var utils = require('./utils');
// 辅助函数
var bind = require('./helpers/bind');
// axios核心库
var Axios = require('./core/Axios');
// 合并配置对象
var mergeConfig = require('./core/mergeConfig');
// 默认属性配置
var defaults = require('./defaults');
function createInstance(defaultConfig) {
var context = new Axios(defaultConfig);
var instance = bind(Axios.prototype.request, context);
utils.extend(instance, Axios.prototype, context);
utils.extend(instance, context);
return instance;
}
var axios = createInstance(defaults);
context是Axios对象的实例。
instance是bind方法返回的一个原函数。
function bind(fn, thisArg) {
return function wrap() {
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
return fn.apply(thisArg, args);
};
};
也就是Axios.prototype.request的拷贝,且改变了this指向(this指向了context),并且拥有参数arguments
var bind = require('./helpers/bind');
function extend(a, b, thisArg) {
forEach(b, function assignValue(val, key) {
if (thisArg && typeof val === 'function') {
a[key] = bind(val, thisArg);
} else {
a[key] = val;
}
});
return a;
}
所以最后导出的instance实际上是一个包含Axios实例属性和方法的函数
这部分的内容,在axios上挂载了许多属性和方法
axios.Axios = Axios;
// 工厂模式,用于创建新的实例
axios.create = function create(instanceConfig) {
return createInstance(mergeConfig(axios.defaults, instanceConfig));
};
// 扩展有关取消请求的属性
axios.Cancel = require('./cancel/Cancel');
axios.CancelToken = require('./cancel/CancelToken');
axios.isCancel = require('./cancel/isCancel');
// 扩展有关并发请求的东西
axios.all = function all(promises) {
return Promise.all(promises);
};
axios.spread = require('./helpers/spread');
// 扩展错误显示
axios.isAxiosError = require('./helpers/isAxiosError');
module.exports = axios;
// 为支持TS
module.exports.default = axios;
到这里,对于axios库入口文件的学习就搞一段落了。这里的内容是总结性的东西,很多模块的具体内容和原理还没有深入的学习和探究,需要在接下来的文章中继续学习。