当前位置: 首页 > news >正文

梅开二度的 axios 源码阅读,三千字详细分享功能函数,帮助扩展开发思维

前言

第一遍看 axios 源码,更多的是带着日常开发的习惯,时不时产生出点联想。

第二遍再看 axios 源码,目标明确,就是奔着函数来的。

当有了明确清晰的目标,阅读速度上来了,思绪也转的飞快

按图索骥,接下来,将和大家一起,找寻 axios 源码中的功能函数,扩展一下开发思路。

文章速读

阅读文章,可以有以下收获:

功能函数

一般基础的工具函数都会放到 utils.js 文件中。前一篇源码阅读中,介绍了关于类型判断的两种方法。

axios 中有许多模块,不同的模块下包含不同的功能。除了 utils.js 文件,这些模块下的文件也包含函数。我称之为功能函数。

功能函数,按照实际功能需要封装的函数,也许并不是常见功能需要的,但是了解之后,没准能帮助今后用到的时候,快速完成开发。

1.standardBrowserEnv 标准浏览器中对 cookie 的读写删操作

axios 拥有对 cookie 操作的能力,就像叶一一会讲故事一样理所当然。

看源码功能之前,我们先来复习关于 cookie 的知识点。

cookie 的格式

通过 document.cookie 获取 cookie 的值,是一串有特定格式的字符串。它的格式是

cookie1=value; cookie2=value; cookie3=value;

了解了 cookie 的格式,对于它的操作,也自然而然的可以接着看代码了。

cookie 的读写删

在标准浏览器中,对 cookie 的处理被封装在了一个立即执行函数里。

(function standardBrowserEnv() {return {/** * cookie 写入(创建)单个写入 * @param {string} name cookie 名 * @param {string} value cookie 值 * @param {*} expires 过期时间 * @param {*} path cookie所在的目录 * @param {*} domain cookie所在的域 * @param {*} secure 是否可以通过HTTP协议的URL设置布尔值 值为 true 时,表示创建的 cookie 只能用 HTTPS 协议发送 */ write: function write(name, value, expires, path, domain, secure) {	// 先将所有的数据存入数组const cookie = [];cookie.push(name + '=' + encodeURIComponent(value));// 如果过期时间有值且是数值型,则存入其转为根据格林威治时间 (GMT) 转成的字符串if (utils.isNumber(expires)) {cookie.push('expires=' + new Date(expires).toGMTString());}	// 当目录存在且为字符串,则存入cookieif (utils.isString(path)) {cookie.push('path=' + path);}// 当域存在且为字符串,则存入cookieif (utils.isString(domain)) {cookie.push('domain=' + domain);}// 是否可以通过HTTP协议的URL设置布尔值为 true 时,则存入cookieif (secure === true) {cookie.push('secure');}	// 将数组转为字符串,并通过 document.cookie 属性来创建 cookiedocument.cookie = cookie.join('; ');},/** * cookie 读取 单个读取 * @param {string} name cookie 名 */read: function read(name) {	// 正则匹配得到数组const match = document.cookie.match(new RegExp('(^|;\s*)(' + name + ')=([^;]*)'));	// match 匹配的返回值中第四个元素为需要的值return match ? decodeURIComponent(match[3]) : null;},	/** * cookie 删除 单个删除 * @param {string} name cookie 名 */remove: function remove(name) {// 将过期时间设置为过去的时间,即可删除 cookie, 这里设置成了前一天(86400000为1天的毫秒数)this.write(name, '', Date.now() - 86400000);},};
})(); 

使用

// 写入
cookies.write('foo', 'bar');
// 删除
cookies.remove('foo');
// 读取
cookies.read('username=John Doe') // => John Doe 

功能汇总

日期转换 toGMTString 方法

toGMTString() 方法可根据格林尼治标准时间 (GMT) 把 Date 对象转换为字符串,并返回结果。

介绍这个方法,主要科普一个新旧交替的世界标准时间。

首先这个 GMT 和 UTC 的定义如下:

GMT(Greenwich Mean Time):格林尼治标准时间。格林尼治是英国伦敦南郊原皇家格林尼治天文台所在地,地球本初子午线的标界处,世界计算时间和经度的起点。格林尼治标准时间过去被当成世界标准的时间。

UTC(Coordinated Universal Time):协调世界时。又称世界统一时间、世界标准时间、国际协调时间。协调世界时是以原子时秒长为基础,在时刻上尽量接近于世界时的一种时间计量系统。协调世界时是现在使用的世界标准时间。

所以在W3C下面有一行提示:

不赞成使用此方法。请使用 toUTCString() 取而代之!!

toUTCString 方法

根据协调世界时 (UTC) 把 Date 对象转换为字符串,并返回结果。

正则 match 方法

match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。

返回值

它的返回值是一个存放匹配结果的数组。该数组的内容依赖于 regexp 是否具有全局标志 g。

  • 如果 regexp 没有标志 g,那么 match() 方法就只能在 stringObject 中执行一次匹配。

    • 如果找到匹配结果,它将返回一个数组,存放了与它找到的匹配文本有关的信息。数组的第1个元素存放的是匹配的文本,其余的元素存放与正则表达式的子表达式匹配的文本和input、index、groups三个元素。* 如果没找到匹配结果,返回 null。
  • 如果 regexp 具有标志 g,则 match() 方法将执行全局检索,找到 stringObject 中的所有匹配子字符串。

    • 如果找到匹配结果,它将返回一个数组,数组的第1个元素存放的是匹配的文本,其余元素存放匹配的一个或多个文本。* 如果没找到匹配结果,返回 null。

小结

1.对 cookie 的读写删操作还是挺简单的,主要是参数要考虑全面。日常开发中使用频率不是很高,但是如果需求涉及到可以帮助快速完成开发。
2.在 standardBrowserEnv 函数中,不难在里面发现 utils 的身影,所以基础工具函数的建设必不可少。
3.把 Date 对象转换为字符串推荐使用 toUTCString 方法。
4.无法删除的属性,可以设置成无效的值,比如这里的通过过期时间提前于当前时间。再比如前一篇介绍过将属性值设置为 undefined。

2.formDataToJSON 抽丝剥茧 formData 与 Object 的转换

FormData 对象

FormData 对象用以将数据编译成键值对,以便用XMLHttpRequest来发送数据。

FormData 对象主要用于发送表单数据,但亦可用于发送带键数据 (keyed data),而独立于表单使用。一般文件流数据的发送,会用到 FormData 对象。

第一条丝线— parsePropPath

matchAll

matchAll 函数会根据入参的正则表达式和字符串,返回所有匹配项的数组。

/*** It takes a regular expression and a string, and returns an array of all the matches** @param {string} regExp - The regular expression to match against.* @param {string} str - The string to search.** @returns {Array<boolean>}*/
const matchAll = (regExp, str) => {let matches;const arr = [];// matches 为每次匹配正确的值while ((matches = regExp.exec(str)) !== null) {arr.push(matches);}return arr;
} 

regExp.exec 方法

在一个指定字符串中执行一个搜索匹配。返回一个结果数组或 null。

匹配的标准用法就是如上功能中的那样,如果需要进一步了解可以阅读 MDN 文档。

parsePropPath

parsePropPath ****函数会将字符串中的字母匹配出来放到数组中。

但看描述和函数还不太确定具体用法,但是经典的来了。

当我们设计的某个函数不好用文字做描述,可以用举例的方式辅助描述。比如下面这个函数,不但举例,还举了多个例子,所以这个函数的用途一目了然。

/*** It takes a string like `foo[x][y][z]` and returns an array like `['foo', 'x', 'y', 'z']** @param {string} name - The name of the property to get.** @returns An array of strings.*/
function parsePropPath(name) {// foo[x][y][z]// foo.x.y.z// foo-x-y-z// foo x y zreturn utils.matchAll(/\w+|[(\w*)]/g, name).map(match => {	// 匹配值为数组,当匹配的第一个元素为空数组时返回空字符串,否则返回第二个元素或第一个元素存在值的那个。return match[0] === '[]' ? '' : match[1] || match[0];});
} 

第二条丝线— arrayToObject

arrayToObject 函数会将数组转换对象。

/*** Convert an array to an object.** @param {Array<any>} arr - The array to convert to an object.** @returns An object with the same keys and values as the array.*/
function arrayToObject(arr) {const obj = {};// 返回数组索引值的数组const keys = Object.keys(arr);let i;const len = keys.length;let key;for (i = 0; i < len; i++) {key = keys[i];obj[key] = arr[key];}return obj;
} 

第三条丝线— forEachEntry

forEachEntry 函数会循环一个可迭代的对象,直到循环结束,把对象的 key 和 value 返回。

/*** For each entry in the object, call the function with the key and value.** @param {Object<any, any>} obj - The object to iterate over.* @param {Function} fn - The function to call for each entry.** @returns {void}*/
const forEachEntry = (obj, fn) => {const generator = obj && obj[Symbol.iterator];const iterator = generator.call(obj);let result;// 循环直到迭代器已将序列迭代完毕while ((result = iterator.next()) && !result.done) {const pair = result.value;fn.call(obj, pair[0], pair[1]);}
}; 

我找了一个可迭代对象测试了一下输出

const data = new Map();data.set('a', 1);
data.set('b', 2);
data.set('c', 3);forEachEntry(data, (name, value) => {let res = name + '-' + value;console.log('name-value:', res);
});// 输出结果
// > name-value: a-1
// > name-value: b-2
// > name-value: c-3 

formDataToJSON

formDataToJSON函数接受 一个 FormData 对象最终返回 JavaScript 对象。

/*** It takes a FormData object and returns a JavaScript object** @param {string} formData The FormData object to convert to JSON.** @returns {Object<string, any> | null} The converted object.*/
function formDataToJSON(formData) {/** * 递归函数 将 FormData 的键值全部插入到给 target 对象 * @param {Array} path FormData 的属性名数组 * @param {string} value FormData 的属性值 * @param {Object} target 最终的目标对象 * @param {number} index FormData 的属性值数组的索引值 * @returns 递归是否中止的布尔值 */function buildPath(path, value, target, index) {let name = path[index++];// isFinite 函数会先将测试值转换为数字,然后再对其进行是否为有限数检测。const isNumericKey = Number.isFinite(+name);const isLast = index >= path.length;name = !name && utils.isArray(target) ? target.length : name;if (isLast) {// 如果检查对象具有该属性,将 value 添加到对应的数组中if (utils.hasOwnProp(target, name)) {target[name] = [target[name], value];} else {target[name] = value;}return !isNumericKey;}if (!target[name] || !utils.isObject(target[name])) {target[name] = [];}const result = buildPath(path, value, target[name], index);if (result && utils.isArray(target[name])) {target[name] = arrayToObject(target[name]);}return !isNumericKey;}// formData 参数是 FormData 类型且 formData.entries 是一个函数if (utils.isFormData(formData) && utils.isFunction(formData.entries)) {const obj = {};utils.forEachEntry(formData, (name, value) => {buildPath(parsePropPath(name), value, obj, 0);});return obj;}return null;
} 

我找 axios 自带的测试实例中的例子打印了一下结果

对象的值是数组

const formData = new FormData();
formData.append('foo', '1');
formData.append('foo', '2');const res = formDataToJSON(formData);
console.log('res', res); // -> { foo: ['1', '2'] } 

对象

const formData = new FormData();formData.append('foo', '1');
formData.append('bar', '2');const res = formDataToJSON(formData);
console.log('res', res); // -> {foo: '1', bar: '2'} 

嵌套对象

const formData = new FormData();formData.append('foo[bar][baz]', '123');const res = formDataToJSON(formData);
console.log('res', res); // -> { foo : { bar : {baz: '123'} } } 

小结

1.formDataToJSON 函数接受 一个 FormData 对象最终返回 JavaScript 对象。
2.FormData() 构造函数用于创建一个新的FormData对象。该函数在 Web 中可用,一些环境会报错“FormData is not define”。
3.FormData 对象转换 JavaScript 对象的功能不是很常用。但是这里的主要收获是,当函数设计的相对复杂的时候,可以用抽丝剥茧的方式,现将支线整理出来,最终拼凑成一个完整的主线。

总结

功能函数会依据实际需求去实现功能。通过分析 axios 源码中的两个重点的功能函数,学习复杂功能如何设计,以及补充了些知识点,还得到目前代码中有些判断条件简化的收获。

很愉快的一次源码阅读体验。

axios 源码阅读历程

捎带分享一下我关于 axios 源码阅读中的历程。

数月前

因为工作中需要弄清楚 axios 的 request 中的入参而在源码中寻找答案,当时想抽时间进行一次源码阅读,但是没有坚持下去,就此作罢。

数日前

前几天做功能联想,重新开始阅读,这次收获颇多。

最后

最近找到一个VUE的文档,它将VUE的各个知识点进行了总结,整理成了《Vue 开发必须知道的36个技巧》。内容比较详实,对各个知识点的讲解也十分到位。



有需要的小伙伴,可以点击下方卡片领取,无偿分享

相关文章:

梅开二度的 axios 源码阅读,三千字详细分享功能函数,帮助扩展开发思维

前言 第一遍看 axios 源码&#xff0c;更多的是带着日常开发的习惯&#xff0c;时不时产生出点联想。 第二遍再看 axios 源码&#xff0c;目标明确&#xff0c;就是奔着函数来的。 当有了明确清晰的目标&#xff0c;阅读速度上来了&#xff0c;思绪也转的飞快。 按图索骥&a…...

vcs仿真教程

VCS是在linux下面用来进行仿真看波形的工具&#xff0c;类似于windows下面的modelsim以及questasim等工具&#xff0c;以及quartus、vivado仿真的操作。 1.vcs的基本指令 vcs的常见指令后缀 sim常见指令 2.使用vcs的实例 采用的是全加器的官方教程&#xff0c;首先介绍不使用…...

java 自定义json解析注解 复杂json解析 工具类

java 自定义json解析注解 复杂json解析 工具类 目录java 自定义json解析注解 复杂json解析 工具类1.背景2、需求-各式各样的json一、一星难度json【json对象中不分层】二、二星难度json【json对象中出现层级】三、三星难度json【json对象中存在数组】四、四星难度json【json对象…...

类的 6 个默认成员函数

文章目录一、构造函数1. 构造函数的定义2. 编译器生成的构造函数3. 默认构造函数4. 初始化列表5. 内置成员变量指定缺省值(C11)二、析构函数1. 析构函数的定义2. 编译器生成的析构函数3. 自己写的析构函数的执行方式三、拷贝构造函数1. C语言值传递和返回值时存在 bug2. 拷贝构…...

基于Verilog HDL的状态机描述方法

⭐本专栏针对FPGA进行入门学习&#xff0c;从数电中常见的逻辑代数讲起&#xff0c;结合Verilog HDL语言学习与仿真&#xff0c;主要对组合逻辑电路与时序逻辑电路进行分析与设计&#xff0c;对状态机FSM进行剖析与建模。 &#x1f525;文章和代码已归档至【Github仓库&#xf…...

6年软件测试经历:成长、迷茫、奋斗

前言 测试工作6年&#xff0c;经历过不同产品、共事过不同专业背景、能力的同事&#xff0c;踩过测试各种坑、遇到过各种bug。测试职场生涯积极努力上进业务和技术能力快速进步过、也有努力付出却一无所得过、有对测试生涯前景充满希望认为一片朝气蓬勃过、也有对中年危机思考不…...

OpenMMLab AI实战营第五次课程

语义分割与MMSegmentation 什么是语义分割 任务&#xff1a; 将图像按照物体的类别分割成不同的区域 等价于&#xff1a; 对每个像素进行分类 应用&#xff1a;无人驾驶汽车 自动驾驶车辆&#xff0c;会将行人&#xff0c;其他车辆&#xff0c;行车道&#xff0c;人行道、交…...

【软考】系统集成项目管理工程师(二十)项目风险管理

一、项目风险管理概述1. 风险概念2. 风险分类3. 风险成本二、项目风险管理子过程1. 规划风险管理2. 识别风险3. 实施定性风险分析4. 实施定量风险分析5. 规划风险应对6. 控制风险三、项目风险管理流程梳理一、项目风险管理概述 1. 风险概念 风险是一种不确定事件或条件,一旦…...

2017-PMLR-Neural Message Passing for Quantum Chemistry

2017-PMLR-Neural Message Passing for Quantum Chemistry Paper: https://arxiv.org/pdf/1704.01212.pdf Code: https://github.com/brain-research/mpnn 量子化学的神经信息传递 这篇文献作者主要是总结了先前神经网络模型的共性&#xff0c;提出了一种消息传递神经网络&am…...

Python:每日一题之全球变暖(DFS连通性判断)

题目描述 你有一张某海域 NxN 像素的照片&#xff0c;"."表示海洋、"#"表示陆地&#xff0c;如下所示&#xff1a; ....... .##.... .##.... ....##. ..####. ...###. ....... 其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿…...

企业级安全软件装机量可能大增

声明 本文是学习大中型政企机构网络安全建设发展趋势研究报告. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 研究背景 大中型政企机构是网络安全保护的重中之重&#xff0c;也是国内网络安全建设投入最大&#xff0c;应用新技术、新产品最多的机构…...

为什么要用频谱分析仪测量频谱?

频谱分析仪是研究电信号频谱结构的仪器&#xff0c;用于信号失真度、调制度、谱纯度、频率稳定度和交调失真等信号参数的测量&#xff0c;可用以测量放大器和滤波器等电路系统的某些参数&#xff0c;是一种多用途的电子测量仪器。从事通信工程的技术人员&#xff0c;在很多时候…...

Python环境搭建、Idea整合

1、学python先要下载什么&#xff1f; 2、python官网 3、idea配置Python 4、idea新建python 学python先要下载什么&#xff1f; python是一种语言&#xff0c;首先你需要下载python&#xff0c;有了python环境&#xff0c;你才可以在你的电脑上使用python。现在大多使用的是pyt…...

HTTP请求返回304状态码以及研究nginx中的304

文章目录1. 引出问题2. 分析问题3. 解决问题4. 研究nginx中的3044.1 启动服务4.2 ETag说明4.3 响应头Cache-Control1. 引出问题 之前在调试接口时&#xff0c;代码总出现304问题&#xff0c;如下所示&#xff1a; 2. 分析问题 HTTP 304: Not Modified是什么意思&#xff1f; …...

【GD32F427开发板试用】使用Arm-2D显示电池电量

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动&#xff0c;更多开发板试用活动请关注极术社区网站。作者&#xff1a;boc 【虽迟但到】 由于快递的原因&#xff0c;11月份申请的&#xff0c;12月1日才收到GD32F427开发板。虽然姗姗来迟&#xff0c;但也没有减少…...

TS第二天 Typesrcipt编译

文章目录自动编译tsconfig.json配置选项include 比较重要excludeextendsfilescompilerOptions 比较重要自动编译 手动模式&#xff1a;每次ts文件修改完&#xff0c;手动编译一次 tsc 01.ts监视模式&#xff1a;ts文件修改完&#xff0c;自动监视编译 tsc 01.ts -w编译所有文…...

基于C#制作一个飞机大战小游戏

此文主要基于C#制作一个飞机大战游戏&#xff0c;重温经典的同时亦可学习。 实现流程1、创建项目2、界面绘制3、我方飞机4、敌方飞机5、子弹及碰撞检测实现流程 1、创建项目 打开Visual Studio&#xff0c;右侧选择创建新项目。 搜索框输入winform&#xff0c;选择windows窗体…...

git修改历史提交(commit)信息

我们在开发中使用git经常会遇到想要修改之前commit的提交信息&#xff0c;这里记录下怎么使用git修改之前已经提交的信息。一、修改最近一次commit的信息 首先通过git log查看commit信息。 我这里一共有6次commit记录。 最新的commit信息为“Merge branch ‘master’ of https:…...

代码解析工具cpg

cpg 是一个跨语言代码属性图解析工具&#xff0c;它目前支持C/C (C17), Java (Java 13)并且对Go, LLVM, python, TypeScript也有支持&#xff0c;在这个项目的根目录下: cpg-core为cpg解析模块的核心功能&#xff0c;主要包括将代码解析为图&#xff0c;core模块只包括对C/C/Ja…...

Linux虚拟机部署Java环境-Jdk-Mysql

Linux虚拟机部署 author hf 1.安装 电脑安装x-shell工具&#xff0c;然后使用堡垒机基础控件windows版进行安装扫描&#xff0c;最后点击自动检测&#xff0c;保证能扫描到X-shell工具的安装路径 使用堡垒机登录快照夏选择工具点击Xshell进行连接 查看linux版本 root:~# ca…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

关于 WASM:1. WASM 基础原理

一、WASM 简介 1.1 WebAssembly 是什么&#xff1f; WebAssembly&#xff08;WASM&#xff09; 是一种能在现代浏览器中高效运行的二进制指令格式&#xff0c;它不是传统的编程语言&#xff0c;而是一种 低级字节码格式&#xff0c;可由高级语言&#xff08;如 C、C、Rust&am…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践

作者&#xff1a;吴岐诗&#xff0c;杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言&#xff1a;融合数据湖与数仓的创新之路 在数字金融时代&#xff0c;数据已成为金融机构的核心竞争力。杭银消费金…...

Golang——7、包与接口详解

包与接口详解 1、Golang包详解1.1、Golang中包的定义和介绍1.2、Golang包管理工具go mod1.3、Golang中自定义包1.4、Golang中使用第三包1.5、init函数 2、接口详解2.1、接口的定义2.2、空接口2.3、类型断言2.4、结构体值接收者和指针接收者实现接口的区别2.5、一个结构体实现多…...

安卓基础(Java 和 Gradle 版本)

1. 设置项目的 JDK 版本 方法1&#xff1a;通过 Project Structure File → Project Structure... (或按 CtrlAltShiftS) 左侧选择 SDK Location 在 Gradle Settings 部分&#xff0c;设置 Gradle JDK 方法2&#xff1a;通过 Settings File → Settings... (或 CtrlAltS)…...

十九、【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建

【用户管理与权限 - 篇一】后端基础:用户列表与角色模型的初步构建 前言准备工作第一部分:回顾 Django 内置的 `User` 模型第二部分:设计并创建 `Role` 和 `UserProfile` 模型第三部分:创建 Serializers第四部分:创建 ViewSets第五部分:注册 API 路由第六部分:后端初步测…...

Xela矩阵三轴触觉传感器的工作原理解析与应用场景

Xela矩阵三轴触觉传感器通过先进技术模拟人类触觉感知&#xff0c;帮助设备实现精确的力测量与位移监测。其核心功能基于磁性三维力测量与空间位移测量&#xff0c;能够捕捉多维触觉信息。该传感器的设计不仅提升了触觉感知的精度&#xff0c;还为机器人、医疗设备和制造业的智…...

Modbus RTU与Modbus TCP详解指南

目录 1. Modbus协议基础 1.1 什么是Modbus? 1.2 Modbus协议历史 1.3 Modbus协议族 1.4 Modbus通信模型 🎭 主从架构 🔄 请求响应模式 2. Modbus RTU详解 2.1 RTU是什么? 2.2 RTU物理层 🔌 连接方式 ⚡ 通信参数 2.3 RTU数据帧格式 📦 帧结构详解 🔍…...

sshd代码修改banner

sshd服务连接之后会收到字符串&#xff1a; SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢&#xff1f; 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头&#xff0c…...