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

    文章

    42

    浏览量

    14993
  • 执行
    +关注

    关注

    0

    文章

    16

    浏览量

    12558
  • 配置
    +关注

    关注

    1

    文章

    184

    浏览量

    18169
收藏 人收藏

    评论

    相关推荐

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

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

    iMatrix平台核心功能—权限管理介绍

    适当的角色而得到这些角色的权限。ACS正是运用了一个用户可以拥有多个角色和一个角色可以分配给多个用户的映射模型极大简化了权限管理。下面具体阐述一下:首先,ACS提供对角色的维护功能,开发者可以创建相互
    发表于 07-11 11:59

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

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

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

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

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

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

    TB-96AI是什么?TB-96AI核心板有哪些核心功能

    、HeadPhone、SPI、I2C、UART、PCIE X4、SDIO、LAN、TF Card、RTC、PWM、ADC、GPIO等丰富的扩展接口,支持用户根据自身需求进行功能扩展。TB-96AI采用10层板
    发表于 06-20 16:28

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

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

    一文看懂python程序的执行过程

    本文主要介绍的是python程序的执行过程,首先介绍的是编译过程,其次介绍的是过程图解及编译字节码,最后介绍了codeobject对象的属性,具体
    发表于 04-26 18:18 1.7w次阅读
    一文看懂python程序的<b class='flag-5'>执行</b><b class='flag-5'>过程</b>

    健康码人脸测温门禁机、“电子哨兵”闸机有哪些核心功能

    检测、门禁与闸机联动管控、健康状态一体化可视化管理等功能,还可以进行体温检测和口罩佩戴检测。健康码人脸测温门禁机的核心功能有:1、精准核验。RK3566,4核2.0
    的头像 发表于 07-25 16:35 440次阅读
    健康码人脸测温门禁机、“电子哨兵”闸机有哪些<b class='flag-5'>核心功能</b>?

    【新品】自动中继无线数传电台,16大核心功能

    新品上市全新一代LoRa数传电台远程配置加密传输自动中继组网RSSI信号强度自动中继无线数传电台——E90-DTU(400SL33)具备十六大核心功能!采用军工级LoRa调制技术的无线数传电台,具有
    的头像 发表于 08-19 10:16 993次阅读
    【新品】自动中继无线数传电台,16大<b class='flag-5'>核心功能</b>!

    应用笔记 | TSMaster核心功能之标定数据的管理

    概述标定模块中,标定数据的管理也是其核心功能。主要包括以下方面的内容:标定数据的载入、标定数据导出、标定数据的刷写,以及配套应用程序的刷写等。下面来详细介绍下这些功能。一、标定数据的载入标定
    的头像 发表于 01-30 09:44 504次阅读
    应用笔记 | TSMaster<b class='flag-5'>核心功能</b>之标定数据的管理

    揭秘网络解码矩阵的五大核心功能

    讯维网络解码矩阵的五大核心功能,包括图像去噪、图像压缩、信号恢复、数字水印和语音编码,以帮助读者更好地了解这一技术的实际应用。 一、图像去噪 在视频传输过程中,由于网络环境等因素的影响,常常会出现图像噪声。这些噪
    的头像 发表于 09-04 09:50 350次阅读
    揭秘网络解码矩阵的五大<b class='flag-5'>核心功能</b>!

    核心功能部件企业,在人形机器人赛道下了盘大棋

    国产零部件厂商们已经跃跃欲试,其中,广州市昊志机电股份有限公司(以下简称“昊志机电”)作为从事中高端数控机床、机器人、新能源汽车核心功能部件的研发设计、生产制造、销售与维修服务的技术企业,其机器人零部件产品齐全且品质卓越 ,在人形机器人赛道角逐中优势显著。
    的头像 发表于 09-18 16:43 599次阅读

    机器视觉的四大核心功能

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

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

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