手写axios源码系列一:axios核心知识点

news2024/10/6 8:23:41

文章目录

  • axios的核心功能
    • 1、axios 函数对象
    • 2、dispatchRequest 发送请求
    • 3、interceptors 拦截器
    • 4、cancelToken 取消请求

在这里插入图片描述

最近从头搭建了一个vue小项目,想使用 axios 作为请求接口的第三方库。结果使用了 axios 这么长时间,想封装一下 axios ,也就是大部分项目中的 src/utils/request.js文件,我居然无法默写出来,create 和 interceptors 这样的 api 都不知道怎么使用;所以决定深入一下 axios 源码,一探究竟 axios 到底是个什么东东。

当前 axios 源码版本为"axios": "^1.3.5"

axios的核心功能

1、axios 函数对象

为什么在这里叫 axios 函数对象

这是因为在 JavaScript 中,函数也是对象,也可以挂载属性以及方法。俗话说:一切皆是对象。

axios使用方式:

  • axios({ method: "post" })
  • axios.post()

由 axios 的使用方式可知,axios 既可以被调用,也可以使用对象的点语法执行请求方法;所以 axios 既是函数又是对象

来个例子:

// 声明axios函数
function axios(config) {
	console.log("配置项:", config)
}
// 为axios挂载方法create
axios.create = () => {
	console.log("axios.create方法")
}
// 为axios挂载属性get
axios.get = "get"

axios({ method: "post" })
axios.create()
console.log("axios的属性:", axios.get)

在这里插入图片描述

我们还可以打印一下 axios :

console.dir(axios)

在这里插入图片描述
可以看到 axios 是一个函数,且身上挂载有 create 方法以及 get 属性。

去看过 axios 的源码就知道,axios 本质上是一个函数,只是将 Axios 类的原型对象方法以及实例对象方法复制到了 axios 函数上面;还有一些其他的属性与方法也挂载到了 axios 函数对象上,如 CancelToken 类、Axios 类等。

2、dispatchRequest 发送请求

在axios中有两种请求方式,根据不同的平台(platform)来使用不同的请求方式:

  1. xhr(XMLHttpRequest),也就是常说的AJAX。(浏览器)
  2. http(Nodejs)

我们只来探究一下浏览器端的就好。

使用 axios 发送 http 请求时,其实底层调用的只有一个方法,就是 Axios.prototype.request 方法。Axios.prototype.request 方法又执行 dispatchRequest 方法,在 dispatchRequest 方法中使用 adapters.getAdapter 方法来区分是使用 xhr 还是 http 发送请求。

流程图:
在这里插入图片描述

dispatchRequest 模块是真正发送请求的地方:

function dispatchRequest(config) {
  // 根据适配器获取发送请求的方式时xhr还是http
  const adapter = adapters.getAdapter(config.adapter || defaults.adapter);
  // 返回promise对象
  return adapter(config).then(
    response => {
      return response
    },
    err => {
      throw new Error(err)
    })
}

3、interceptors 拦截器

在项目中,一般发送 http 请求时会对请求和响应进行一些特殊处理:判断 token,设置请求头,统一处理响应错误信息等。如果要挨个对每个请求都做处理的话太麻烦,方便起见,axios 提供了拦截器。

拦截器分为:

  • 请求拦截器 axios.interceptors.request
  • 响应拦截器 axios.interceptors.response

请求拦截器的使用:

axios.interceptors.request.use(function (config) {
	// 设置token
	const token = sessionStorage.getItem("token");
	if (token) { config.headers.token = token };
	//返回config配置项
	return config;
}, function (error) {
	return Promise.reject(error);
})

响应拦截器的使用:

axios.interceptors.response.use(function (response) {
	// 全局统一设置错误信息提示,code、msg是与后台约定好的返回值
	const { code, msg } = response.data;
	if (code === 0){ 
		return response.data;
	} else {
		Notify({ type: 'danger', msg, duration: 5000 });
		return Promise.reject(new Error(msg || 'Error'));
	}
}, function (error) {
	const { message} = error.response.data;
	Notify({ type: 'danger', message, duration: 5000 });
	return Promise.reject(error);
})

拦截器可以设置多个,例如:

// 请求拦截器1
axios.interceptors.request.use(function one(config){
	console.log("请求拦截器 1");
	return config;
},function one(error){
	return Promise.reject(error);
})
// 请求拦截器2
axios.interceptors.request.use(function two(config){
	console.log("请求拦截器 2");
	return config;
},function two(error){
	return Promise.reject(error);
})
// 响应拦截器1
axios.interceptors.response.use(function one(response){
	console.log("响应拦截器 1");
	return response;
},function one(error){
	return Promise.reject(error);
})
// 响应拦截器2
axios.interceptors.response.use(function two(response){
	console.log("响应拦截器 2");
	return response;
},function two(error){
	return Promise.reject(error);
})

当我发送一个请求时,会按照以下顺序打印出结果:

请求拦截器 2
请求拦截器 1
响应拦截器 1
响应拦截器 2
response

为什么会先打印请求拦截器 2,然后再打印请求拦截器 1 呢?

这里就涉及到源码中拦截器的执行了。

源码 Axios.prototype.request 方法中定义了一条执行链 chain = [dispatchRequest, undefined]。然后获取请求拦截器,使用array.unshift() 将其添加到执行链 chain 的头部;获取响应拦截器,使用 array.push() 将其添加到执行链的尾部,就形成了最终的执行顺序:

chain = [
	two(config),
	two(error),
	one(config),
	one(error),
	dispatchRequest,  // 发送请求
	undefined,
	one(response),
	one(error),
	two(response),
	two(error),
]

4、cancelToken 取消请求

请求既可以发送,也可以进行取消,取消请求使用的是 xhr.abort() 方法,abort 就是中止的意思。

取消请求 | Axios 中文文档 | Axios 中文网

取消请求的方式有两种:

  1. 使用 CancelToken(官方在 axios 版本0.22.0 开始被弃用,但还是做了兼容处理,仍可使用)

    const CancelToken = axios.CancelToken;
    let source = CancelToken.source();
    axios.get(url, {
    	cancelToken: source.token
    }).then(response => {
    	// ...
    })
    // 取消请求
    source.cancel();
    
  2. 使用 AbortController(官方推荐的使用方式,而且非常简单)

    const controller = new AbortController();
    axios.get(url, {
    	signal: controller.signal
    }).then(response => {
    	// ...
    })
    // 取消请求
    controller.abort();
    

    AbortController 接口表示一个控制器对象,允许你根据需要中止一个或多个 Web 请求

    下图为 AbortController 类:

    在这里插入图片描述
    可以看出,abort 为 AbortController 类的原型对象的方法,signal 是 AbortSignal 类生成的实例对象。

在 axios 源码中,取消请求封装了一个 CancelToken 类,使用发布/订阅模式进行取消请求的统一集中处理。在AJAX发送请求时,对取消请求 onCanceled 方法进行了订阅,在 CancelToken 内部使用 promise 对象向外暴露了 resolve 方法,在执行 source.cancel() 取消请求时,其实是执行了内部暴露的 resolve 方法,resolve 执行,则 promise 的状态由 pending 变为 fulfilled,然后 promise.then() 方法执行,而订阅的 onCanceled 方法就是放在 promise.then() 中去执行的。

// xhr.js
const xhr = new XMLHttpRequest();
onCanceled = () => {
	xhr.abort();
}
config.cancelToken.subscribe(onCanceled)
// CancelToken.js
class CancelToken {
	constructor(executor){
		let resolvePromise;
		this._listeners = [];
		this.promise = new Promise(resolve => {
			resolvePromise = resolve;  // 向外暴露resolve方法
		})
		// executor执行器函数执行,传入参数为一个函数
		executor(function c(){
			resolvePromise();  // 执行resolve方法
		})
		this.promise.then(()=>{
			let len = this._listeners.length;
			while(len-- > 0){
				this._listeners[i](); // 执行onCanceled()
			}
		})
	}
	subscribe(fn){
		this._listeners.push(fn);  //收集依赖
	}
}

不理解不要紧,因为取消请求逻辑在我看来是 axios 源码里面最绕人,最难理解的知识点,因为这里使用到了设计模式、闭包、promise异步编程、高阶函数等知识点,你只要有其中一个知识点模糊,估计就有点麻烦了,只能多学多看。

放张图理解一下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/445738.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nacos2.2.2开启鉴权配置

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、更改application.properties中的配置二、修改配置 前言 最近公司开启了一个新的电商项目,项目中用到了Naocs作为注册中心和配置中心&#xff0…

WindowsUbuntu下python程序打包

Python程序的运行必须要有Python的环境,但是程序编出来是用的,如果是给别人用,而他/她的电脑上又没有Python程序运行的环境怎么办呢?总不能让他/她去安装一个吧?这时我们就要将Python程序打包为exe可执行文件&#xff…

【文章学习系列之模型】PatchTST

本章内容 文章概况模型结构实验结果长期预测表征学习 消融实验分块和通道独立性不同的回顾窗口 总结 文章概况 《A Time Series is Worth 64 Words: Long-term Forecasting with Transformers》是2023年发表于ICLR的一篇文章。该文章借鉴了计算机视觉领域的Vision Transformer…

树形DP分析

树形dp 简单来说树形 d p 就是在树上做 d p 罢了 简单来说树形dp就是在树上做dp罢了 简单来说树形dp就是在树上做dp罢了 树嘛,就要符合除了根节点外每个节点只有一个父节点 树嘛,就要符合除了根节点外每个节点只有一个父节点 树嘛,就要符合除…

# 从车灯模组的角度聊聊信息安全需求

文章目录 1. 前言2.信息安全需求2.1 硬件安全2.1.1 接口安全2.1.2 主板安全2.1.3 芯片安全 2.3 系统安全2.3.1 代码安全2.3.2 软件读保护2.3.3 安全启动2.3.4 安全升级2.3.5 安全诊断 2.4 通信安全2.5 数据安全 3. 安全启动流程3.1 基于签名技术的安全启动方案3.2 基于对称签名…

netty源码阅读--处理客户端请求

背景 netty是一个非常成熟的NIO框架,众多apache的顶级项目底层通信框架都是用的是netty,本系列博客主要是记录自己复习netty源码的过程,重在理解netty的关键如:如何启动,如何接受网络数据、netty的内存管理机制以及编解码器等&am…

Python学习笔记--面向对象

未完待续。。。。。 (一)面向对象的基本概念 我们之前学习的编程方式就是面向过程的 面相过程和面相对象,是两种不同的编程方式 对比面向过程的特点,可以更好地了解什么是面向对象 1.1过程和函数 过程是早期的一个编程概念 过程…

4.3 分段低次插值

学习目标: 如果我要学习分段低次插值,我可能会采取以下几个步骤: 学习插值的基本概念和方法 在学习分段低次插值之前,我需要先掌握插值的基本概念和方法,例如拉格朗日插值、牛顿插值和内维尔方法等。这些基础知识可…

C#调用C++封装的SDK库(dll动态库)——上

C#调用C封装的SDK库(dll动态库)——上 一、C封装库 通过前几篇文章,我们封装了C的动态DLL库,有Qt版的,有C版的,当然还有介绍了Pimpl模式在SDK封装中的使用: Qt创建SDK VS创建SDK Pimple在SDK封装中的应用 但是&a…

关于逻辑回归的几个函数

写作业时重新理了下,如果有问题欢迎指正! 说是回归,其实就是个分类,用【0,1】标记结果y是录取还是录取,而影响结果y的就是X(x0,x1,…xn-1)。怎么判断结果y是0还是1用到的是逻辑回归函数(也叫假…

java企业级信息系统开发学习笔记05 初探Spring AOP

文章目录 一、学习目标二、Spring AOP(一)AOP基本含义(二)AOP基本作用(三)AOP和OOP对比(四)AOP使用方式(五)AOP基本概念 三、采用配置方法使用AOP&#xff08…

windows下Tomcat安装

目录 1.安装java环境 2.配置Tomcat环境变量 3.安装服务 4.启动前修改配置文件 (1)设置tomcat端口 (2)设置临时日志等文件夹的位置 5.放入应用 6.启动Tomcat服务 1.安装java环境 安装tomcat版本对应的JDK 比如:…

Mysql的简介和选择

文章目录 前言一、为什么要使用数据库 数据库的概念为什么要使用数据库二、程序员为什么要学习数据库三、数据库的选择 主流数据库简介使用MySQL的优势版本选择四、Windows 平台下安装与配置MySQL 启动MySQL 服务控制台登录MySQL命令五、Linux 平台下安装与配置MySQL总结 前言…

Redis 分布式锁解决方案

1 背景 我们日常在电商网站购物时经常会遇到一些高并发的场景,例如电商 App 上经常出现的秒杀活动、限量优惠券抢购,还有我们去哪儿网的火车票抢票系统等,这些场景有一个共同特点就是访问量激增,虽然在系统设计时会通过限流、异步…

设计模式:行为型模式 - 责任链模式

文章目录 1.概述2.结构3.案例实现4.优缺点5.源码解析 1.概述 在现实生活中,常常会出现这样的事例:一个请求有多个对象可以处理,但每个对象的处理条件或权限不同。例如,公司员工请假,可批假的领导有部门负责人、副总经…

链表方式实现栈和队列(C语言)

目录 栈 队列 栈 栈(Stack)是一种常见的数据结构,它是一种线性数据结构,具有后进先出(Last In First Out, LIFO)的特点。栈可以看作是一个容器,只能在容器的一端进行插入和删除操作&#xff0c…

【12 Filter 学习笔记】

Filter 笔记记录 1. Filter 过滤器2. Filter介绍3. FilterChain3. Filter 过滤器的使用4. 过滤器使用细节5. Filter的声明周期6. FilterConfig7. 过滤器五种拦截行为 1. Filter 过滤器 2. Filter介绍 3. FilterChain 3. Filter 过滤器的使用 import javax.servlet.ServletExcep…

SQL:group by 的用法及其与 over(partition by)的区别

1.group by的作用 根据一定的规则将一个数据集划分成若干个小区域,然后针对每个区域进行数据处理。即分组查询,一般是和聚合函数配合使用。 重点说明(重要): 如果用Select选择某个字段,那么这个字段要么在G…

cpld 模拟iic 访问原理实现

1,因为 单片机访问sfp和ds125占用了6路iic,但是现在还有lm75需要使用iic访问,所以要么使用iic片选,但是扩展iic需要的总线不多,这里要么考虑使用互斥锁,也就是使用同一路iic时候加锁,使用完成后就释放。也可以考虑使用cpld模拟iic来访问lm75。 2,cpld 软件模拟iic总线原理…

初识MySql数据库

​​​​使用MySql数据库的英文打字小游戏: 英文打字小游戏 MySql学习 1:MySql是关系型数据库,跨平台的。Sql Server是微软出的啊。 2:Web服务器端运行原理图。 Web服务器:网页资源的获取 游戏服务器:游戏数据的同步…