当前位置: 代码网 > it编程>编程语言>Javascript > 详解axios跨端架构是如何实现的

详解axios跨端架构是如何实现的

2024年05月26日 Javascript 我要评论
介绍我们都知道,axios 是是一个跨平台请求方案,在浏览器端采用 xmlhttprequest api 进行封装,而在 node.js 端则采用 http/https 模块进行封装。axios 内部

介绍

我们都知道,axios 是是一个跨平台请求方案,在浏览器端采用 xmlhttprequest api 进行封装,而在 node.js 端则采用 http/https 模块进行封装。axios 内部采用适配器模式将二者合二为一,在隐藏了底层的实现的同时,又对外开放了一套统一的开放接口。

那么本文,我们将来探讨这个话题:axios 的跨端架构是如何实现的?

从 axios 发送请求说起

我们先来看看 axios 是如何发送请求的。

// 发送一个 get 请求
axios({ 
  method: 'get',
  url: 'https://jsonplaceholder.typicode.com/comments'
  params: { postid: 1 }
}) 

// 发送一个 post 请求
axios({
  method: 'post'
  url: 'https://jsonplaceholder.typicode.com/posts',
  data: {
    title: 'foo',
    body: 'bar',
    userid: 1,
  }
})

dispatchrequest() 方法

当使用 axios 请求时,实际上内部是由 axios 实例的 .request() 方法处理的。

// /v1.6.8/lib/core/axios.js#l38
async request(configorurl, config) {
    try {
      return await this._request(configorurl, config);
    } catch (err) {}
}

而 ._request() 方法内部会先将 configorurl, config 2 个参数处理成 config 参数。

// /v1.6.8/lib/core/axios.js#l62
_request(configorurl, config) {
    if (typeof configorurl === 'string') {
      config = config || {};
      config.url = configorurl;
    } else {
      config = configorurl || {};
    }

    // ...
}

这里是为了同时兼容下面 2 种调用方法。

// 调用方式一
axios('https://jsonplaceholder.typicode.com/posts/1')
// 调用方式二
axios({
  method: 'get',
  url: 'https://jsonplaceholder.typicode.com/posts/1'
})

当然,这不是重点。在 ._request() 方法内部请求最终会交由 dispatchrequest() 处理。

// /v1.6.8/lib/core/axios.js#l169-l173
try {
  promise = dispatchrequest.call(this, newconfig);
} catch (error) {
  return promise.reject(error);
}

dispatchrequest() 是实际调用请求的地方,而实际调用是采用 xmlhttprequest api(浏览器)还是http/https 模块(node.js),则需要进一步查看。

// /v1.6.8/lib/core/dispatchrequest.js#l34
export default function dispatchrequest(config) { /* ... */ }

dispatchrequest() 接收的是上一步合并之后的 config 参数,有了这个参数我们就可以发送请求了。

跨端适配实现

// /v1.6.8/lib/core/dispatchrequest.js#l49
const adapter = adapters.getadapter(config.adapter || defaults.adapter);

这里就是我们所说的 axios 内部所使用的适配器模式了。

axios 支持从外出传入 adapter 参数支持自定义请求能力的实现,不过很少使用。大部分请求下,我们都是使用内置的适配器实现。

defaults.adapter

defaults.adapter 的值如下:

// /v1.6.8/lib/defaults/index.js#l40
adapter: ['xhr', 'http'],

adapters.getadapter(['xhr', 'http']) 又是在做什么事情呢?

适配器实现

首先,adapters 位于 lib/adapters/adapters.js

所属的目录结构如下:

可以看到针对浏览器和 node.js 2 个环境的适配支持:http.js、xhr.js。

adapters 的实现如下。

首先,将内置的 2 个适配文件引入。

// /v1.6.8/lib/adapters/adapters.js#l2-l9
import httpadapter from './http.js';
import xhradapter from './xhr.js';

const knownadapters = {
  http: httpadapter,
  xhr: xhradapter
}

knownadapters 的属性名正好是和 defaults.adapter 的值 ['xhr', 'http'] 是一一对应的。

而 adapters.getadapter(['xhr', 'http']) 的实现是这样的:

// /v1.6.8/lib/adapters/adapters.js#l27-l75
export default {
  getadapter: (adapters) => {
    // 1)
    adapters = array.isarray(adapters) ? adapters : [adapters];

    let nameoradapter;
    let adapter;
    
    // 2)
    for (let i = 0; i < adapters.length; i++) {
      nameoradapter = adapters[i];
      adapter = nameoradapter;
      
      // 3)
      if (!isresolvedhandle(nameoradapter)) {
        adapter = knownadapters[string(nameoradapter).tolowercase()];
      }

      if (adapter) {
        break;
      }
    }

    // 4)
    if (!adapter) {
      throw new axioserror(
        `there is no suitable adapter to dispatch the request `,
        'err_not_support'
      );
    }

    return adapter;
  }
}

内容比较长,我们会按照代码标准的序号分 4 个部分来讲。

1)、这里是为了兼容调用 axios() 时传入 adapter 参数的情况。

// `adapter` allows custom handling of requests which makes testing easier.
// return a promise and supply a valid response (see lib/adapters/readme.md).
adapter: function (config) {
  /* ... */
},

因为接下来 adapters 是作为数组处理,所以这种场景下,我们将 adapter 封装成数组 [adapters]

// /v1.6.8/lib/adapters/adapters.js#l28
adapters = array.isarray(adapters) ? adapters : [adapters];

2)、接下来,就是遍历 adapters 找到要用的那个适配器。

到目前为止,adapters[i](也就是下面的 nameoradapter)既可能是字符串('xhr''http'),也可能是函数(function (config) {})。

// /v1.6.8/lib/adapters/adapters.js#l37
let nameoradapter = adapters[i];
adapter = nameoradapter;

3)、那么,我们还要检查 nameoradapter 的类型。

// /v1.6.8/lib/adapters/adapters.js#l42-l48
if (!isresolvedhandle(nameoradapter)) {
  adapter = knownadapters[(id = string(nameoradapter)).tolowercase()];
}

isresolvedhandle() 是一个工具函数,其目的是为了判断是否要从 knownadapters 获取适配器。

// /v1.6.8/lib/adapters/adapters.js#l24
const isresolvedhandle = (adapter) => typeof adapter === 'function' || adapter === null || adapter === false;

简单理解,只有 adapter 是字符串的情况('xhr''http'),isresolvedhandle(nameoradapter) 才返回 false,才从 knownadapters 获得适配器。

typeof adapter === 'function' || adapter === null 这个判断条件我们容易理解,这是为了排除自定义 adapter 参数(传入函数或 null)的情况。

而 adapter === false 又是对应什么情况呢?

那是因为我们的代码只可能是在浏览器或 node.js 环境下运行。这个时候 httpadapter 和 xhradapter 具体返回是有差异的。

// /v1.6.8/lib/adapters/xhr.js#l48
const isxhradaptersupported = typeof xmlhttprequest !== 'undefined';
export default isxhradaptersupported && function (config) {/* ...*/}

// /v1.6.8/lib/adapters/http.js#l160
const ishttpadaptersupported = typeof process !== 'undefined' && utils.kindof(process) === 'process';
export default ishttpadaptersupported && function httpadapter(config) {/* ... */}

也就是说:在浏览器环境 httpadapter 返回 false,xhradapter 返回函数;在 node.js 环境 xhradapter 返回 false,httpadapter 返回函数。

因此,一旦 isresolvedhandle() 逻辑执行完成后。

if (!isresolvedhandle(nameoradapter)) {/* ... */}

会检查 adapter 变量的值,一旦有值(非 false)就说明找到适配器了,结束遍历。

if (adapter) {
  break;
}

4)、最终在返回适配器前做空检查

// 4)
if (!adapter) {
  throw new axioserror(
    `there is no suitable adapter to dispatch the request `,
    'err_not_support'
  );
}

return adapter;

如此,就完成了跨端架构的实现。

总结

本文我们讲述了 axios 的跨端架构原理。axios 内部实际发出请求是通过 dispatchrequest() 方法处理的,再往里看则是通过适配器模式取得适应于当前环境的适配器函数。

axios 内置了 2 个适配器支持:httpadapter 和 xhradapter。httpadapter 是 node.js 环境实现,通过 http/https 模块;xhradapter 这是浏览器环境实现,通过 xmlhttprequest api 实现。node.js 环境 xhradapter 返回 false,浏览器环境 httpadapter 返回 false——这样总是能返回正确的适配器。

以上就是详解axios跨端架构是如何实现的的详细内容,更多关于axios跨端架构的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com