这词怎么读?
官方的GitHub参考专门有人问过这个问题 #802 ,高票赞同的读法是:acks--ee--oh-ss
。用中文的相似音是: 哎克 C 欧斯
如果我们去写一个网络库,需要考虑什么(只考虑web端)
1、支持哪些请求方式(get、post等)
2、请求配置(通用、自定义 eg. 超时、编码)
3、支持异步、并行
4、拦截器(请求发出前拦截、请求返回后拦截)
5、取消正在发出或未发出的请求
6、请求结果回调(正确、其他的)
先参看下axios的目录结构:
├── adapters
│ ├── README.md
│ ├── http.js #node环境http对象
│ └── xhr.js #web环境http对象
├── axios.js #入口
├── cancel
│ ├── Cancel.js #取消的构造对象类
│ ├── CancelToken.js #取消操作的包装类
│ └── isCancel.js #工具类
├── core
│ ├── Axios.js #Axios实例对象
│ ├── InterceptorManager.js #拦截器控制器
│ ├── README.md
│ ├── buildFullPath.js #拼接请求的url、baseURL
│ ├── createError.js #创建异常信息类工具
│ ├── dispatchRequest.js #默认的拦截器(分发完全请求的拦截器)
│ ├── enhanceError.js #异常信息类实体
│ ├── mergeConfig.js #合并配置文件(用户设置的和默认的)
│ ├── settle.js #根据http-code值来resolve/reject状态
│ └── transformData.js #转转请求或相应的数据的工具类
├── defaults.js #默认配置类
├── helpers/ #一些帮助方法
└── utils.js #通用的工具类
其核心模块主要有两个,一个是adapters 实际请求的发出的模块,另外一个就是core里面实现了一个请求从创建到完成的整个流程控制。adapters模块是对XMLHttpRequest的包装,这里不过过道的概述(主要是对一些细节的处理)。先看一下大体的流程:
接下来,我们从源码的角度来看看一个请求是如何被发出的,下面以get请求发出到收到相应为例(axios.get()):
导包
导包时发生了什么:
创建了一个axios实例,并添加了一些方法。其关键方是在createInstance()
:
function createInstance(defaultConfig) {
// 创建一个Axios实例
var context = new Axios(defaultConfig);
// 为request方法bind上Axios,并方法一个包装的wrap方法:可以简单理解成创建了一个对象。之所以这样创建为了方便能 axios('https://m.zz.cn/a')这样用
var instance = bind(Axios.prototype.request, context);
// 把 Axios.prototype 上的方法拓展到instance上
utils.extend(instance, Axios.prototype, context);
// 把context中的defaults、interceptors拓展到instance实例中
utils.extend(instance, context);
return instance;
}
导包成功后,开始调用其方法了:
axios
.get(URL, {
params: {
foo: 'bar'
}
})
.then(handleSuccess, handleFailure2)
.catch(handleFailure);
在调用get
方法时是在中封装的,其内部最终会调到Axios.prototype.request
指向的函数中:
Axios.prototype.request = function request(config) {
/*eslint no-param-reassign:0*/
// Allow for axios('example/url'[, config]) a la fetch API
if (typeof config === 'string') {
config = arguments[1] || {};
config.url = arguments[0];
} else {
config = config || {};
}
config = mergeConfig(this.defaults, config);
// Set config.method
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
var chain = [dispatchRequest, undefined];
var promise = Promise.resolve(config);
this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
chain.unshift(interceptor.fulfilled, interceptor.rejected);
});
this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
chain.push(interceptor.fulfilled, interceptor.rejected);
});
while (chain.length) {
promise = promise.then(chain.shift(), chain.shift());
}
return promise;
};
这里面巧妙之处创建了一个请求处理链 china
,创建了Promise.resolve()
,然后通过promise.then()
把请求链串起来。
这个请求链中有个默认添加的重要的一环dispatchRequest
,它会根据不同的平台(其实在new Axios()已经确定使用哪个adapter了)来发出网络请求,并承担了对请求前后的数据包装工作。
function getDefaultAdapter() {
var adapter;
if (typeof XMLHttpRequest !== 'undefined') {
// For browsers use XHR adapter
adapter = require('./adapters/xhr');
} else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
// For node use HTTP adapter
adapter = require('./adapters/http');
}
return adapter;
}
接下来就是对后续拦截器的调用了,最终会回调到发起请求的地方。