axios是怎么做到适配xhr和http的

前言

我们在使用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通过判断环境和适配器列表选择适配器,发送相应请求。
相关推荐
也无晴也无风雨1 小时前
深入剖析输入URL按下回车,浏览器做了什么
前端·后端·计算机网络
Martin -Tang2 小时前
Vue 3 中,ref 和 reactive的区别
前端·javascript·vue.js
FakeOccupational3 小时前
nodejs 020: React语法规则 props和state
前端·javascript·react.js
放逐者-保持本心,方可放逐3 小时前
react 组件应用
开发语言·前端·javascript·react.js·前端框架
曹天骄4 小时前
next中服务端组件共享接口数据
前端·javascript·react.js
阮少年、5 小时前
java后台生成模拟聊天截图并返回给前端
java·开发语言·前端
郝晨妤6 小时前
鸿蒙ArkTS和TS有什么区别?
前端·javascript·typescript·鸿蒙
AvatarGiser6 小时前
《ElementPlus 与 ElementUI 差异集合》Icon 图标 More 差异说明
前端·vue.js·elementui
喝旺仔la6 小时前
vue的样式知识点
前端·javascript·vue.js
别忘了微笑_cuicui6 小时前
elementUI中2个日期组件实现开始时间、结束时间(禁用日期面板、控制开始时间不能超过结束时间的时分秒)实现方案
前端·javascript·elementui