0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

核心功能具体的执行过程-2

汽车电子技术 来源:知码前端 作者:清清玄 2023-03-01 09:59 次阅读

前言

上一节我们简单的介绍了一下 axios 的整体加载流程和使用过程。可以清楚的了解到当 import axios from 'axios' 之后 这背后到底做了什么。并且我们也简单介绍了一个 axios 到底是一个什么类型的数据。以及为什么可以即可以当成方法调用还可以通过对象的调用方式调用某些属性方法

如果没有了解的同学可以先去看一下上一篇文章的介绍,再来继续往下看。


这篇我们主要讲解一下 axios 中的 配置、拦截器和执行链等一些核心的功能到底是怎么运行的。

02

配置过程

要了解这个之前,我们先来看一下 axios 在使用的时候一种方式:

axios.create({ ...配置项 })

不知道大家有没有使用过种方式,这种方式可以让我们传递一些配置到 axios 的内部,具体实现如下:

axios.create = function create(instanceConfig) {
  return createInstance(mergeConfig(axios.defaults, instanceConfig));
};

没错,最终又调用了 createInstance 函数,再来看一下函数体吧:

function createInstance(defaultConfig) {
  var context = new Axios(defaultConfig);
  var instance = bind(Axios.prototype.request, context);

  // Copy axios.prototype to instance
  utils.extend(instance, Axios.prototype, context);

  // Copy context to instance
  utils.extend(instance, context);

  return instance;
}

有一个函数需要关注一下就是 mergeConfig, 这个函数会把 axios 自带的配置和我们传入的配置进行合并,我们传入的配置会覆盖 axios 自带的配置,也就是说我们传入的配置优先级会更高。

由于这个 mergeConfig 函数体太大,我们就不细说了,大家有兴趣可以看一下源码。

这里要继续说一下,我们在发送某个具体的请求的时候也可以进行配置,这样就有三个配置。

优先级依次是:某个具体请求配置 > 创建实例对象配置 > axios 默认配置

03

上节说过,axios可以像对象那样调用属性方法,如 get、post等,其实最终都会调用 request 方法,代码如下:

utils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: (config || {}).data
    }));
  };
});

utils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {
  /*eslint func-names:0*/
  Axios.prototype[method] = function(url, data, config) {
    return this.request(mergeConfig(config || {}, {
      method: method,
      url: url,
      data: data
    }));
  };
});

可以看出最终都会调用到 this.request 方法。那我们来重点看一下 request方法具体做了什么。

先看一下函数体吧,代码也不是很多:

Axios.prototype.request = function request(config) {
  /*eslint no-param-reassign:0*/
  // Allow for axios('example/url'[, config]) a la fetch API
  if (typeof config === 'string') {
    config = arguments[1] || {};
    config.url = arguments[0];
  } else {
    config = config || {};
  }

  config = mergeConfig(this.defaults, config);

  // Set config.method
  if (config.method) {
    config.method = config.method.toLowerCase();
  } else if (this.defaults.method) {
    config.method = this.defaults.method.toLowerCase();
  } else {
    config.method = 'get';
  }

  // Hook up interceptors middleware
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);

  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
    chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }

  return promise;
};

主要有三点:

1、生成配置项

2、生成拦截器、执行链

3、返回执行链的结果

下面我们重点介绍一下 2 是如何生成拦截器和执行链的

每个axios实例都会有一个 interceptors 属性,如下:

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}

interceptors里面存放着 request 拦截器和response拦截器。InterceptorManager 中有一个 handlers 属性,是一个数组存放着具体的拦截器,再来看一个比较熟悉的方法:

InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};

相信大家肯定用过这个 use 方法,这个方法接收两个函数类型的参数,再封装成一个对象放到 handlers中。
再回到 request 函数体中,看一下

  this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {
      chain.unshift(interceptor.fulfilled, interceptor.rejected);
  });

  this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {
    chain.push(interceptor.fulfilled, interceptor.rejected);
  });

通过遍历把 handlers 的拦截器都放到一个 chain 中,尤其要注意:this.interceptors.request 这个操作,是把最后的拦截器放到 chain的最前面。最终形成以下链接:

poYBAGP-sDKAOH_5AADszCSjWMI370.png

当然这还不是最终的 chain,因为前面

var chain = [dispatchRequest, undefined];

有这样行代码,所以最终的 chain 应该是下面的:

poYBAGP-sDyAKYAzAAEKkVnDApk539.png

这才是一个最终的 chain 。也就是说我们执行的每个请求都是执行了一个链,最终返回了一个 promise对象,是不是感觉也没有那么神秘,看一下执行代码,很简单

varpromise=Promise.resolve(config);
while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }
return promise;

以上便是 axios 发送某个请求的全过程,那么接下来我们继续看一下到底是怎么发送的请求。

04

具体请求

从上面我们可以看到axios发送的请求就是一个链的执行过程,除去 request 和 response的拦截器不说,我们重点说一下:dispatchRequest 这个方法的执行过程,因为具体的请求就是在这个方法中执行的。先来看一下源码:

module.exports = function dispatchRequest(config) {
  throwIfCancellationRequested(config);

  // Ensure headers exist
  config.headers = config.headers || {};

  // Transform request data
  config.data = transformData(
    config.data,
    config.headers,
    config.transformRequest
  );

  // Flatten headers
  config.headers = utils.merge(
    config.headers.common || {},
    config.headers[config.method] || {},
    config.headers
  );

  utils.forEach(
    ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],
    function cleanHeaderConfig(method) {
      delete config.headers[method];
    }
  );

  var adapter = config.adapter || defaults.adapter;

  return adapter(config).then(function onAdapterResolution(response) {
    throwIfCancellationRequested(config);

    // Transform response data
    response.data = transformData(
      response.data,
      response.headers,
      config.transformResponse
    );

    return response;
  }, function onAdapterRejection(reason) {
    if (!isCancel(reason)) {
      throwIfCancellationRequested(config);

      // Transform response data
      if (reason && reason.response) {
        reason.response.data = transformData(
          reason.response.data,
          reason.response.headers,
          config.transformResponse
        );
      }
    }

    return Promise.reject(reason);
  });
};

方法本身并不难理解,处理一下请求头然后再通过转换器转一下请求数据,最后通过一个适配器执行请求。下面我们再看一下适配器是什么,看一下下面的代码

  var adapter = config.adapter || defaults.adapter;

适配器是通过配置获取的,平时的开发中我们几乎不需要自己定义适配器,一般都是用系统默认的,所以我们看一下默认的适配器是怎么样的。下面是默认配置的代码:

adapter: getDefaultAdapter(),

继续看:

function getDefaultAdapter() {  var adapter;
  if (typeof XMLHttpRequest !== 'undefined') {
    // For browsers use XHR adapter
    adapter = require('./adapters/xhr');
  } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {
    // For node use HTTP adapter
    adapter = require('./adapters/http');
  }
  return adapter;
}

看到这里应该大体的有点明白了吧,其实就是我们平时用的 XMLHttpRequest 对象,那为什么还要做一个适配器呢,主要是因为 axios 不仅仅是一款可以用在 浏览器的库,在 node 开发中也可以使用,但node中没有 XMLHttpRequest对象,就得通过其它的方式实现。本文不涉及 node,所以我们主要看以下代码

adapter=require('./adapters/xhr');

因为代码比较多,所以这里我用图片的形式展示一下:

pYYBAGP-sHmAU4Q7AAGPpBiaU2A722.png

到这里,我们才真正看到了熟悉的 XMLHttpRequest对象。其实axios底层也就是用的 XMLHttpRequest对象而已,没有什么神秘的。只不过人家封装的很好用起来方便。

其实到这里我们就已经把 axios的整体源码分析了一次,当然还有很多细节没有说到,比如:错误处理,状态码处理等,大家有兴趣的可以自己去细读源码。只有自己阅读一次才能更好的理解 axios的优雅之处。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 核心
    +关注

    关注

    0

    文章

    44

    浏览量

    15026
  • 执行
    +关注

    关注

    0

    文章

    16

    浏览量

    12601
  • 配置
    +关注

    关注

    1

    文章

    188

    浏览量

    18375
收藏 人收藏

    评论

    相关推荐

    5G电视或成下代5G无线通信服务核心功能

    电视以及视讯内容可能会成为下一代5G无线通信服务的核心功能。..
    发表于 02-17 08:14 1382次阅读

    阿里云数据库POLARDB核心功能物理复制技术解读

    深入解读阿里云数据库POLARDB核心功能物理复制技术
    发表于 06-02 10:16

    智能数字钟的核心功能定位

    目录前言… 2第一章 需求定义… 10产品功能定义… 10智能数字钟的核心功能定位… 14芯片选型… 15基于成本约束的设计思路… 15CPU的选型… 15音乐芯片的选型… 16天气预报语音播报芯片
    发表于 07-30 07:19

    F103的功能分类核心功能是什么

    文章目录F103的功能分类核心功能:缺一不可,缺少任何一个都不能工作。重要功能:根据每一款单片机的不同,具有不同的偏重点。多为帮助内核做一些内核不能做的事情。通信功能:单片机行业成熟,
    发表于 12-10 07:33

    Bifrost GPU可编程核心的顶级布局、优势和着色器核心功能

    本指南介绍了典型的马里Bifrost GPU可编程核心(第三代马里GPU)的顶级布局、优势和着色器核心功能。Bifrost家族包括Mali-G30、Mali-G50和Mali-G70系列产品。 在
    发表于 08-02 17:52

    江智机器人人机语音交互技术核心功能点探索

    江智机器人人机语音交互技术核心功能点探索无疑机器人人机语音交互技能是人工智能机器人必须具有的核心功能点之一。国内的科大讯飞,百度等为代表的一些已在人机语音技术方面耕耘了多年,且取得了较大的发展。国内
    的头像 发表于 03-06 14:51 889次阅读
    江智机器人人机语音交互技术<b class='flag-5'>核心功能</b>点探索

    机器视觉的四大核心功能

    机器视觉的四大核心功能  机器视觉是一种通过电子系统和计算机软件实现人类视觉功能的技术。它运用计算机视觉、模式识别、图像处理和机器学习等技术,以摄像机和图像处理技术为基础,将图像转化为数字信号,并
    的头像 发表于 12-25 11:15 1002次阅读

    智慧灌区平台功能全面解析(智慧灌区场景和核心功能

    全面解析。() 一、智慧灌区平台业务场景和核心功能 智慧灌区平台主要业务应用于水资源规划、相关设备监管、灌区用水计划、耕地土壤增减方面。其核心功能包括在灌区区域部署传感器设备对水文、土壤等信息实时采集,然后上传到云平
    的头像 发表于 02-22 10:27 627次阅读
    智慧灌区平台<b class='flag-5'>功能</b>全面解析(智慧灌区场景和<b class='flag-5'>核心功能</b>)

    [天拓四方]工业边缘网关的核心功能、应用场景和实施策略

    重要支持。本文将重点介绍工业边缘网关的核心功能、应用场景和实施策略,以展示其在工业自动化领域的专业性和实用性。 一、工业边缘网关的核心功能 工业边缘网关的核心功能包括: 数据采集与集成:边缘网关能够实时采集来自各种工业设
    的头像 发表于 05-23 16:29 364次阅读

    深度解析:蓝牙网关核心功能以及应用场景

    为可通过互联网传输的数据格式,从而使得远程监控、数据分析和设备控制成为可能。 二、蓝牙网关的核心功能 蓝牙网关的核心功能主要围绕以下几个方面展开: 扫描与连接 :自动搜索并连接周围的蓝牙设备,无论是蓝牙低功耗(BLE)设备还
    的头像 发表于 07-10 10:16 837次阅读
    深度解析:蓝牙网关<b class='flag-5'>核心功能</b>以及应用场景

    万界星空科技低代码云mes核心功能详解!建议收藏!

    云MES系统作为数字化转型的关键组成部分,具有一系列核心功能和优势,可以极大地改善制造流程管理和生产执行效率。
    的头像 发表于 08-06 09:34 235次阅读
    万界星空科技低代码云mes<b class='flag-5'>核心功能</b>详解!建议收藏!

    光伏电站智能运维管理系统三大核心功能

    光伏电站智能运维管理系统三大核心功能 以往,光伏电站的运维工作需要人工巡检和维护,不仅费时费力,而且效率低下。而随着智能技术的发展,陕西公众智能监测自主研发了光伏电站智能运维管理系统,为光伏电站运维
    的头像 发表于 08-23 15:55 409次阅读
    光伏电站智能运维管理系统三大<b class='flag-5'>核心功能</b>

    PCS储能变流器工作原理与核心功能

    PCS储能变流器是一种双向电流可控转换装置,它能够连接储能电池系统与电网或负荷。这种装置的核心功能是控制储能电池的充电和放电过程,实现交直流电能的变换。在无电网情况下,PCS储能变流器甚至可以直接为
    的头像 发表于 09-17 17:03 916次阅读

    工业物联网盒子核心功能与技术特点

    将这些数据上传至云端或本地服务器进行分析处理。工业物联网盒子不仅是数据采集的前端,也是实现设备远程监控、预测性维护、能效优化等高级应用的基础。 一、核心功能与技术特点 工业物联网盒子的核心功能与技术特点,使其能
    的头像 发表于 11-22 17:21 195次阅读

    Monitor Wafer的核心功能、特点、生产流程和应用

    制造中扮演着至关重要的角色。它并不直接用于最终产品的制造,而是作为一种过程监控工具,用于实时或周期性地“诊断”设备和工艺的健康状况,确保整个生产流程的稳定性和可靠性。 一、Monitor Wafer的核心功能
    的头像 发表于 12-06 10:59 278次阅读