三步法解析Axios源码

简介: 三步法解析Axios源码

一、领悟思想


Axios是一个基于Promise的HTTP库,根据官网介绍,有以下几个特点:


  1. 在浏览器端会创建XMLHttpRequests
  2. 在Node端会创建HTTP请求
  3. 由于Axios是一个基于Promise的HTTP库,所以其支持Promise API
  4. 支持请求和响应拦截器
  5. 支持请求和响应数据转换
  6. 支持取消请求
  7. 自动转换JSON数据
  8. 客户端支持防御XSRF攻击


通过上述官网介绍的特点,我认为其突出的优点有三个:


  1. 支持Promise API,可以方便进行链式调用;
  2. 支持请求和响应拦截器,该拦截器将Node中中间件思想引入该库,在请求发送之前和响应接收之后可以对其进行处理。
  3. 支持数据转换器,转换器主要负责数据发送前以及响应接收后对数据的处理。


二、把握设计



理解了该库设计的特点,下面从源码目录、抽象接口及核心设计原理三个层面对Axios进行整体的把握。

2.1 源码目录


如下所示是Axios的源码目录及各个文件的作用

640.png


2.2 抽象接口

640.png

2.3 设计原理


首先看一段代码,这段代码的执行顺序包含着Axios的核心原理。


axios.defaults.baseURL = 'http://localhost:8080'
// 请求拦截器一
axios.interceptors.request.use(
    config => {
        console.log('请求拦截器一', config);
        return config;
    },
    error => {
        console.log('request interceptor rejected1');
        return Promise.reject(error);
    }
);
// 请求拦截器二
axios.interceptors.request.use(
    config => {
        console.log('请求拦截器二', config);
        return config;
    },
    error => {
        console.log('request interceptor rejected2');
        return Promise.reject(error);
    }
);
// 响应拦截器一
axios.interceptors.response.use(
    response => {
        console.log('响应拦截器一', response);
        return response;
    },
    error => {
        console.log('response interceptor rejected1');
        return Promise.reject(error);
    }
);
// 响应拦截器二
axios.interceptors.response.use(
    response => {
        console.log('响应拦截器二', response);
        return response;
    },
    error => {
        console.log('response interceptor rejected2');
        return Promise.reject(error);
    }
);
axios('/', {
    method: 'post',
    headers: {
        'Content-Type': 'application/json'
    },
    data: {
        test: 'test'
    },
    // 请求转换器
    transformRequest: [(data, headers) => {
        console.log('请求转换器', data);
        return JSON.stringify(data)
    }],
    // 响应转换器
    transformResponse: [(response, headers) => {
        console.log('响应转换器', response);
        return response;
    }]
})
.then((response) => {
    console.log(response.data)
})

写了这么多代码,大家肯定对这段代码的执行结果很感兴趣,为了满足各位看客的好奇心,下面就直接抛出来这段结果。

640.png

不过单看执行结果也不能了解其核心设计原理呀,老铁别急,其实小小代码就已经包含了Axios的整个执行过程,通过观察结果及代码可以将整个过程简化为下图:

640.jpg


其核心原理就是这个吗?是的,你没有看错,这就是Axios的核心设计原理,通过一系列链式的处理就能够得到所需要的结果。


三、体会细节



宏观的事聊完了,下面就详细聊几个核心细节吧:整个流程、请求/响应拦截器、dispatchRequest是个啥、请求/响应数据转换器。

3.1 整体运行流程


在第二章中阐述了该核心原理,老铁们一定对该整体是如何运转起来的很感兴趣吧,下面就来解答各位老铁的疑惑——Axios

function Axios(instanceConfig) {
  this.defaults = instanceConfig;
  // 拦截器实例化
  this.interceptors = {
    request: new InterceptorManager(),
    response: new InterceptorManager()
  };
}
// 通过一系列的继承绑定操作,该函数其实就是axios函数
Axios.prototype.request = function request(config) {
  // ……
  config = mergeConfig(this.defaults, config);
  // Set config.method
  // ……
  // ****核心****
  // 存储该调用链的数组
  var chain = [dispatchRequest, undefined];
  var promise = Promise.resolve(config);
  // 将请求拦截器的内容塞到数组前面(注意用的unshift函数,这就很好的解释了为什么先调用的请求拦截器后执行)
  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);
  });
  // 利用Promise将整个数组中的内容串起来,这样就可以按照顺序链式执行了
  while (chain.length) {
    promise = promise.then(chain.shift(), chain.shift());
  }
  return promise;
};

是不是很巧妙?通过利用数组先来存储需要的内容,先处理的在数组的前面(请求拦截器),后处理的在数组的后面(响应拦截器),然后利用Promise将整个内容串起来,很好的处理网络请求属于异步的问题——Perfect。

3.2 请求/响应拦截器


通过观察第二部分的执行结果我们已经了解了请求/响应拦截器,下面就做一下总结:

  1. 请求拦截器就是在发送请求前执行的回调函数,个人认为其最大功用就是对多个请求的配置进行统一修改
  2. 仔细观察发现请求拦截器1先加入但是后执行,是不是与整体运行流程中的代码对上了。
  3. 响应拦截器就是在请求得到响应后执行的回调函数,成功回调的参数就是响应response,其可以对多个请求的响应进行统一修改。


先抛出请求/响应拦截器的核心代码

function InterceptorManager() {
  this.handlers = [];
}
// 注册拦截器
InterceptorManager.prototype.use = function use(fulfilled, rejected) {
  this.handlers.push({
    fulfilled: fulfilled,
    rejected: rejected
  });
  return this.handlers.length - 1;
};
// 删除拦截器
InterceptorManager.prototype.eject = function eject(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null;
  }
};
// 对拦截器进行分发
InterceptorManager.prototype.forEach = function forEach(fn) {
  utils.forEach(this.handlers, function forEachHandler(h) {
    if (h !== null) {
      fn(h);
    }
  });
};

看看拦截器的核心源码,是不是发现与一种设计模式很像?对的,就是观察者模式。当调用use方法的时候就会将回调函数(成功、失败)保存至handlers属性上,方便后期的调用;当调用eject方法的时候就会删除对应索引位置回调函数;当调用forEach方法的时候就会就会对handlers属性(存储的拦截器回调)中的内容进行分发。

3.3 dispatchRequest是个啥


前面聊了整个请求的请求前(请求拦截器)和请求后(响应拦截器),是不是感觉少点东西,如何发请求,这就是我们本次要与大家一起唠的dispatchRequest(config)。

module.exports = function dispatchRequest(config) {
  // ……
  //请求数据转换
  config.data = transformData(
    config.data,
    config.headers,
    config.transformRequest
  );
  // ……
  // 获取适配器:自己配置了就选自己的,自己没有设置就选默认的(浏览器端就选xhrAdapter、node端就选httpAdapter;这也就是为什么Axios即支持浏览器又支持Node的原因)
  var adapter = config.adapter || defaults.adapter;
  return adapter(config).then(function onAdapterResolution(response) {
    // ……
    // 响应数据转换器
    response.data = transformData(
      response.data,
      response.headers,
      config.transformResponse
    );
    return response;
  }, function onAdapterRejection(reason) {
    if (!isCancel(reason)) {
      // ……
      // 响应数据转换器
      if (reason && reason.response) {
        reason.response.data = transformData(
          reason.response.data,
          reason.response.headers,
          config.transformResponse
        );
      }
    }
    return Promise.reject(reason);
  });
};

通过观察整个请求流程中的中间环节——dispatchRequest,它一共做了三件事:

  1. 调用请求数据转换器转换请求数据


  1. 选择合适的适配器发起请求——自己配置了就选自己的,自己没有配置就选默认的(浏览器端就选xhrAdapter、node端就选httpAdapter;这也就是为什么Axios即支持浏览器又支持Node的原因)


  1. 当请求数据返回后,调用响应数据转换器转换响应数据


3.4 请求/响应数据转换器


既然3.3中提到了请求/响应转换器,本节就来聊一聊它俩。

// 核心源码
module.exports = function transformData(data, headers, fns) {
  utils.forEach(fns, function transform(fn) {
    data = fn(data, headers);
  });
  return data;
};

请求数据转换调用,实质上就是利用请求数据转换器对请求头和请求数据进行特定的处理(transformRequest为处理函数的数组,defaults中包含默认的配置)

config.data = transformData(
  config.data,
  config.headers,
  config.transformRequest
);

响应数据转换调用类似于请求数据转换调用,对响应体进行一系列的处理(transformResponse为处理函数的数组,defaults中包含默认的配置)

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

四、结语



上述三章对Axios进行整体的分析,从Axios的特点、整体设计及关键环节三个方面进行了讲述,通过阅读源码学到了很多知识,也能够更加熟练的使用Axios。为了保证各位老铁的学习Axios源码的效果,对学习Axios源码的两条建议:

  1. 边阅读本文边看源码,能够有更深入的理解。


  1. 不要纠结于具体的实现,从宏观的角度去看源码,这样能够节省大量时间。









相关文章
|
6月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
599 29
|
6月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
175 4
|
6月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
6月前
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。
|
6月前
|
存储 前端开发 JavaScript
在线教育网课系统源码开发指南:功能设计与技术实现深度解析
在线教育网课系统是近年来发展迅猛的教育形式的核心载体,具备用户管理、课程管理、教学互动、学习评估等功能。本文从功能和技术两方面解析其源码开发,涵盖前端(HTML5、CSS3、JavaScript等)、后端(Java、Python等)、流媒体及云计算技术,并强调安全性、稳定性和用户体验的重要性。
|
6月前
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
9月前
|
存储 设计模式 算法
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
|
9月前
|
设计模式 存储 安全
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
|
7月前
|
机器学习/深度学习 自然语言处理 算法
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
生成式 AI 大语言模型(LLMs)核心算法及源码解析:预训练篇
1151 0
|
8月前
|
自然语言处理 数据处理 索引
mindspeed-llm源码解析(一)preprocess_data
mindspeed-llm是昇腾模型套件代码仓,原来叫"modelLink"。这篇文章带大家阅读一下数据处理脚本preprocess_data.py(基于1.0.0分支),数据处理是模型训练的第一步,经常会用到。
243 0

热门文章

最新文章

  • 1
    vue引入axios出现Module parse failed: Unexpected token (5:2)
    434
  • 2
    基于SqlSugar的开发框架循序渐进介绍(10)-- 利用axios组件的封装,实现对后端API数据的访问和基类的统一封装处理
    172
  • 3
    Vue中的axios深度探索:从基础安装到高级功能应用的全面指南
    260
  • 4
    JavaScript 使用axios库发送 post请求给后端, 给定base64格式的字符串数据和一些其他参数, 使用表单方式提交, 并使用onUploadProgress显示进度
    1056
  • 5
    若依修改,若依如何发送请求---王清江07,axios的请求在request.js文件中,若依发送GET请求,必须用param
    585
  • 6
    axios发送post请求,如何接受和返回一个axios的字段,解决bug的方法,困难的事情先从简单做起,先从发送一个axios的post请求做起,解决方法查别人的资料,查看F12看network就行
    125
  • 7
    文本,前后端数据交互,简单请求,如何去给data数据赋值,在mounted()里赋值,利用axios发送的请求,res就是数据集,就是后端的数据,this.users = res.data.data
    106
  • 8
    Request failed with status code 400,使用axios.post要发送参数,认真比对原项目,看看有没有忘记什么?
    339
  • 9
    vue3 在 watchEffect 里中断未完成的 axios 请求(只保留最后一次请求的方法---连续点击查询按钮的优化)
    253
  • 10
    前后端数据交互.js文件的axios的写法,想要往后端发送数据,页面注入API,await的意思是同步等待服务器数据,并返回,axios注入在其他页面,其他页面调用的时候,同步作用
    137
  • 推荐镜像

    更多
  • DNS