前言
我们在使用axios的时候是不需要判断这个环境和指定发送的方法的,但是它既可以发送浏览器环境下xhr请求,也可以发送node环境下http请求,就来看一下这是怎么实现的。
基础结构
arduino
------lib
|------adpters // 适配器
|------ adapters.js // 适配器接口
|------ http.js // http适配器,发送http请求
|------ xhr.js // xhr适配器,发送ajax请求
|------cancel
|------CanceledError.js // 当操作被取消是抛出该错误类
|------CancelToken.js // 定义了CancelToken类和取消请求的方法
|------isCancel.js // 判断是否取消请求
|------core
|------Axios.js // 定义了Axios类,包含发送请求(request)和获取url的方法,提供多种使用request的方法
|------AxiosError.js //当请求出现错误时,提供精确的信息
|------AxiosHeader.js //定义处理请求头的方法
|------buildFullpath.js //如果请求的url不完整,将baseUrl和requestUrl拼接完整
|------despatchRequest.js //发送请求
|------InterceptorManager.js // 管理拦截器,管理一个栈,该栈的元素由拦截器组成,定义了使用和清除拦截器的方法。
|------mergeConfig.js // 合并配置项。
|------settle.js // 根据请求返回数据的status决定promise的状态是reject还是resolve
|------transformData.js // 格式化数据,
|------defaults
|------index.js //默认配置
|------transitional
|------env
|------helper // 一些工具
|------plaform // 浏览器和node环境
|------axios.js // 实例化Axios类
|------utils.js // 一些工具
发送请求
通常我们使用axios发送请求使用的方法就是 axios.request(configOrUrl,config)
或者 axios.get(url,config)
axios是Axios的实例,在Axios类中,有一个很重要的方法 request()
, 其他的get,post
等方法都是依赖request方法改造来的
首先,我们看一下Axios类中有什么
js
// /lib/core/Axios.js
class Axios {
constructor(instanceConfig) {
this.defaults = instanceConfig; // 构造实例是传过来的配置参数参数
this.interceptors = {
request: new InterceptorManager(), // 请求拦截器
response: new InterceptorManager() // 响应拦截器
};
}
// 请求函数
request(config){
/** 请求处理 */
// 发送请求
promise = dispatchRequest.call(this, newConfig);
}
// 得到完整的url
getUri(config){/**/}
}
request()是 发送请求的方法,之后get(),post()等方法封装request()并挂载在Axios原型对象中。从而可以通过 axios.get()
来发送请求
request()方法处理请求头,拦截请求,发送请求,拦截响应。我们忽略拦截器,不妨直接看 dispatchRequest()
js
// lib/core/dispatchRequest
export default function dispatchRequest(config) {
const adapter = adapters.getAdapter(config.adapter || defaults.adapter);
return adapter(config).then(function onAdapterResolution(response) {
// 处理响应
}
}
dispatchRequest() 转换请求数据、 选择adapter
并发送请求、 转换响应数据。
这个adapter是什么呢?
是适配器。发送数据有多种环境,我们常用的就是node环境和浏览器环境,但是我们在使用axios时是不用区分是何种环境的,原因就在于axios做了适配。
通过adapters.getAdapter()
得到一个函数,这个函数才是真正发送请求的地方。实际上可以返回的函数有两个
- httpAdapter
- xhrAdapter
xhr适配器
js
const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
// 如果不支持xhr请求将会返回false
export default isXHRAdapterSupported && function (config) {
return new Promise(function dispatchXhrRequest(resolve, reject) {
// 新建请求
let request = new XMLHttpRequest();
// 打开请求
request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
function onload(){}
// 发送请求
request.send(requestData || null);
}
http适配器
js
// lib/adapter/http
const isHttpAdapterSupported = typeof process !== 'undefined' && utils.kindOf(process) === 'process';
export default isHttpAdapterSupported && function httpAdapter(config) {
/*eslint no-async-promise-executor:0*/
// 返回一个promise对象
return new Promise(async function dispatchHttpRequest(resolvePromise, rejectPromise) {
// 配置处理
const options = {... };
// 创建请求 相当于http.request()/https.request()
req = transport.request(options, function handleResponse(res){
/**响应处理**/
const response = {...};
})
// 发送请求
if (utils.isStream(data)) {
data.pipe(req);
}else {
req.end()
}
}
调用适配器
适配器模式讲究的就是一个兼容,从外部来看我们并不关心它具体是什么请求。具体是怎么实现的呢?
发送请求的大致过程
js
\\ lib/adapters/adapter
const knownAdapters = {
http: httpAdapter, // lib/adapter/http导出的函数,发送http请求的函数
xhr: xhrAdapter // 同样是发送xhr请求的函数
}
\\ lib\core\diaptchRuest
function dispactchRequest(){
const adapter = adapters.getAdapter(config.adapter || defaults.adapter);
return adpater(config).then(){...} // 调用相应的适配器
}
getAdapter() 的具体实现
js
// 参数adapters 是适配器的名称,默认值是 ['xhr','http'] 与knownAdapters的属性值相对应
getAdapter: (adapters) => {
// 如果传入的adapter不是数组就转化为数组
adapters = utils.isArray(adapters) ? adapters : [adapters];
for (let i = 0; i < length; i++) {
nameOrAdapter = adapters[i];
if (!isResolvedHandle(nameOrAdapter)) {
adapter = knownAdapters[(id = String(nameOrAdapter)).toLowerCase()];
}
if (adapter) {
break;
}
}
}
getAdapter 的实现就是传入适配器列表,找到最先可以执行的适配器,将适配器返回。 默认的适配器列表是 ['xhr','http']
从中可以看出xhr的优先级是要比http请求的优先级高的。
总结
- 无论是xhr适配器还是http适配器,都是一个接收config返回promise的函数,结构上是一致的,因此可以通过适配器模式兼容他们,在使用axios的时候就不用考虑是什么环境。
- axios通过判断环境和适配器列表选择适配器,发送相应请求。