前言
我们在使用axios的时候是不需要判断这个环境和指定发送的方法的,但是它既可以发送浏览器环境下xhr请求,也可以发送node环境下http请求,就来看一下这是怎么实现的。
基础结构
1——lib
2|——adpters
3 |—— adapters.js
4 |—— http.js
5 |—— xhr.js
6|——cancel
7 |——CanceledError.js
8 |——CancelToken.js
9 |——isCancel.js
10|——core
11 |——Axios.js
12 |——AxiosError.js
13 |——AxiosHeader.js
14 |——buildFullpath.js
15 |——despatchRequest.js
16 |——InterceptorManager.js
17 |——mergeConfig.js
18 |——settle.js
19 |——transformData.js
20 |——defaults
21 |——index.js
22|——transitional
23|——env
24|——helper
25|——plaform
26|——axios.js
27|——utils.js
28
发送请求
通常我们使用axios发送请求使用的方法就是 axios.request(configOrUrl,config)
或者 axios.get(url,config)
axios是Axios的实例,在Axios类中,有一个很重要的方法 request()
, 其他的get,post
等方法都是依赖request方法改造来的
首先,我们看一下Axios类中有什么
1
2class Axios {
3 constructor(instanceConfig) {
4 this.defaults = instanceConfig;
5 this.interceptors = {
6 request: new InterceptorManager(),
7 response: new InterceptorManager()
8 };
9 }
10
11 request(config){
12
13
14 promise = dispatchRequest.call(this, newConfig);
15 }
16
17 getUri(config){}
18}
request()是 发送请求的方法,之后get(),post()等方法封装request()并挂载在Axios原型对象中。从而可以通过 axios.get()
来发送请求
request()方法处理请求头,拦截请求,发送请求,拦截响应。我们忽略拦截器,不妨直接看 dispatchRequest()
1
2export default function dispatchRequest(config) {
3
4 const adapter = adapters.getAdapter(config.adapter || defaults.adapter);
5
6 return adapter(config).then(function onAdapterResolution(response) {
7
8 }
9}
dispatchRequest() 转换请求数据、 选择adapter
并发送请求、 转换响应数据。
这个adapter是什么呢?
是适配器。发送数据有多种环境,我们常用的就是node环境和浏览器环境,但是我们在使用axios时是不用区分是何种环境的,原因就在于axios做了适配。
通过adapters.getAdapter()
得到一个函数,这个函数才是真正发送请求的地方。实际上可以返回的函数有两个
- httpAdapter
- xhrAdapter
xhr适配器
1const isXHRAdapterSupported = typeof XMLHttpRequest !== 'undefined';
2
3
4export default isXHRAdapterSupported && function (config) {
5 return new Promise(function dispatchXhrRequest(resolve, reject) {
6
7 let request = new XMLHttpRequest();
8
9 request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);
10 function onload(){}
11
12 request.send(requestData || null);
13 }
http适配器
1
2const isHttpAdapterSupported = typeof process !== 'undefined' && utils.kindOf(process) === 'process';
3
4export default isHttpAdapterSupported && function httpAdapter(config) {
5
6
7 return new Promise(async function dispatchHttpRequest(resolvePromise, rejectPromise) {
8
9 const options = {... };
10
11 req = transport.request(options, function handleResponse(res){
12
13 const response = {...};
14 })
15
16 if (utils.isStream(data)) {
17 data.pipe(req);
18 }else {
19 req.end()
20 }
21 }
调用适配器
适配器模式讲究的就是一个兼容,从外部来看我们并不关心它具体是什么请求。具体是怎么实现的呢?
发送请求的大致过程
1\\ lib/adapters/adapter
2const knownAdapters = {
3 http: httpAdapter,
4 xhr: xhrAdapter
5}
6
7\\ lib\core\diaptchRuest
8function dispactchRequest(){
9 const adapter = adapters.getAdapter(config.adapter || defaults.adapter);
10
11 return adpater(config).then(){...}
12}
13
getAdapter() 的具体实现
1
2getAdapter: (adapters) => {
3
4 adapters = utils.isArray(adapters) ? adapters : [adapters];
5 for (let i = 0; i < length; i++) {
6 nameOrAdapter = adapters[i];
7 if (!isResolvedHandle(nameOrAdapter)) {
8 adapter = knownAdapters[(id = String(nameOrAdapter)).toLowerCase()];
9 }
10 if (adapter) {
11 break;
12 }
13 }
14}
15
getAdapter 的实现就是传入适配器列表,找到最先可以执行的适配器,将适配器返回。 默认的适配器列表是 ['xhr','http']
从中可以看出xhr的优先级是要比http请求的优先级高的。
总结
- 无论是xhr适配器还是http适配器,都是一个接收config返回promise的函数,结构上是一致的,因此可以通过适配器模式兼容他们,在使用axios的时候就不用考虑是什么环境。
- axios通过判断环境和适配器列表选择适配器,发送相应请求。