前言

我们在使用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通过判断环境和适配器列表选择适配器,发送相应请求。
个人笔记记录 2021 ~ 2025