
Axios 是一个基于 Promise 的 HTTP 客户端,广泛应用于前端开发中,用于发送 HTTP 请求。其源码结构清晰,其中 Helpers 工具库包含了许多实用的工具函数,这些函数为 Axios 的核心功能提供了有力的支持。
本文将深入阅读 axios-1.x/lib/helpers 下的所有文件,并对每个文件的功能进行详细解析。

模块类型 | 典型方法 | 核心能力 |
|---|---|---|
URL构造器 | buildURL/combineURLs | 参数序列化与URL合成 |
数据转换 | formDataToJSON/toFormData | 多格式数据互转 |
环境检测 | isAbsoluteURL/cookies | 跨平台环境特征识别 |
异步控制 | spread/callbackify | Promise与回调模式的桥梁 |
AxiosTransformStream.js这个文件主要实现了一个自定义的转换流 AxiosTransformStream,继承自 TransformStream。它允许在数据传输过程中对数据进行转换。例如,可以对请求数据进行加密,对响应数据进行解密等操作。
// AxiosTransformStream.js
class AxiosTransformStream extends TransformStream {
constructor(transformer) {
super({
transform(chunk, controller) {
const transformedChunk = transformer(chunk);
controller.enqueue(transformedChunk);
}
});
}
}
export default AxiosTransformStream;设计思路
设计的核心思想是提供一个可插拔的转换机制,让开发者可以根据需要自定义数据转换逻辑。通过继承 TransformStream,可以方便地集成到浏览器的流处理机制中。
重点逻辑
transform 方法:在这个方法中,调用传入的 transformer 函数对数据块进行转换,并将转换后的数据块加入到输出流中。AxiosURLSearchParams.js该文件实现了一个自定义的 URLSearchParams 类,扩展了原生的 URLSearchParams 功能。它支持更多的数据类型,如数组、对象等的序列化。
// AxiosURLSearchParams.js
class AxiosURLSearchParams extends URLSearchParams {
append(key, value) {
if (Array.isArray(value)) {
value.forEach(v => super.append(key, v));
} else if (typeof value === 'object') {
super.append(key, JSON.stringify(value));
} else {
super.append(key, value);
}
}
}
export default AxiosURLSearchParams;设计思路
原生的 URLSearchParams 只支持简单的数据类型,如字符串和数字。为了支持更复杂的数据类型,如数组和对象,设计了这个自定义类。
重点逻辑
append 方法:根据传入值的类型进行不同的处理。如果是数组,则将数组中的每个元素依次添加;如果是对象,则将对象序列化为 JSON 字符串后添加。bind.jsbind.js 文件实现了一个 bind 函数,用于绑定函数的 this 值。这与 JavaScript 原生的 Function.prototype.bind 方法类似。
// bind.js
'use strict';
export default function bind(fn, thisArg) {
return function wrap() {
return fn.apply(thisArg, arguments);
};
}设计思路
在 JavaScript 中,函数的 this 值是动态绑定的,有时需要固定 this 值。这个 bind 函数就是为了实现这个功能,方便在不同的上下文中调用函数。
重点逻辑
wrap 函数:返回一个新的函数,在调用时使用 apply 方法将 this 值绑定为 thisArg,并将参数传递给原始函数 fn。buildURL.js该文件实现了一个 buildURL 函数,用于构建完整的 URL,包括拼接查询参数。
// buildURL.js
import AxiosURLSearchParams from './AxiosURLSearchParams.js';
export default function buildURL(url, params) {
if (!params) {
return url;
}
const searchParams = new AxiosURLSearchParams(params);
const serializedParams = searchParams.toString();
if (serializedParams) {
const hashIndex = url.indexOf('#');
if (hashIndex !== -1) {
url = url.slice(0, hashIndex);
}
url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;
}
return url;
}设计思路
在发送 HTTP 请求时,需要将查询参数拼接到 URL 上。buildURL 函数就是为了方便地完成这个任务,同时处理 URL 中的哈希部分。
重点逻辑
AxiosURLSearchParams:使用自定义的 AxiosURLSearchParams 类来序列化查询参数。callbackify.jscallbackify.js 文件实现了一个 callbackify 函数,用于将一个返回 Promise 的函数转换为回调风格的函数。
// callbackify.js
export default function callbackify(fn) {
return function callbackified(...args) {
const callback = args.pop();
fn(...args)
.then(result => callback(null, result))
.catch(error => callback(error));
};
}设计思路
在一些旧的代码中,可能仍然使用回调函数来处理异步操作。callbackify 函数可以将现代的 Promise 风格的函数转换为回调风格的函数,方便与旧代码兼容。
重点逻辑
combineURLs.js该文件实现了一个 combineURLs 函数,用于合并两个 URL。
// combineURLs.js
export default function combineURLs(baseURL, relativeURL) {
return baseURL.replace(/\/+$/, '') + '/' + relativeURL.replace(/^\/+/, '');
}设计思路
在配置 Axios 时,通常会设置一个基础 URL 和一个相对 URL。combineURLs 函数用于将这两个 URL 合并成一个完整的 URL。
重点逻辑
composeSignals.jscomposeSignals.js 文件实现了一个 composeSignals 函数,用于组合多个 AbortSignal 对象。
// composeSignals.js
export default function composeSignals(...signals) {
const controller = new AbortController();
signals.forEach(signal => {
if (signal && signal.aborted) {
controller.abort();
} else if (signal) {
signal.addEventListener('abort', () => controller.abort());
}
});
return controller.signal;
}设计思路
在某些情况下,需要同时监听多个 AbortSignal 对象的状态。composeSignals 函数可以将多个 AbortSignal 对象组合成一个新的 AbortSignal 对象,方便统一管理。
重点逻辑
abort 事件:遍历所有的 AbortSignal 对象,监听其 abort 事件。如果某个 AbortSignal 已经被取消,则立即取消新的 AbortSignal。cookies.js该文件实现了一些与 cookie 操作相关的函数,如获取、设置和删除 cookie。
// cookies.js
export function getCookie(name) {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
export function setCookie(name, value, days) {
let expires = '';
if (days) {
const date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
expires = `; expires=${date.toUTCString()}`;
}
document.cookie = `${name}=${value || ''}${expires}; path=/`;
}
export function deleteCookie(name) {
setCookie(name, '', -1);
}设计思路
在浏览器环境中,经常需要对 cookie 进行操作。这些函数提供了简单的 API 来获取、设置和删除 cookie。
重点逻辑
getCookie 函数:通过分割 document.cookie 字符串来获取指定名称的 cookie 值。setCookie 函数:根据传入的名称、值和过期天数设置 cookie。deleteCookie 函数:通过设置过期时间为过去的时间来删除 cookie。deprecatedMethod.js该文件实现了一个 deprecatedMethod 函数,用于标记某个方法已被弃用,并在调用时发出警告。
// deprecatedMethod.js
export default function deprecatedMethod(name, replacement, fn) {
return function deprecated(...args) {
console.warn(`Method ${name} is deprecated. Use ${replacement} instead.`);
return fn(...args);
};
}设计思路
当某个方法不再推荐使用时,需要给开发者一个提示。deprecatedMethod 函数可以将一个方法标记为已弃用,并在调用时输出警告信息。
重点逻辑
console.warn 输出警告信息。formDataToJSON.js该文件实现了一个 formDataToJSON 函数,用于将 FormData 对象转换为 JSON 对象。
// formDataToJSON.js
export default function formDataToJSON(formData) {
const json = {};
for (const [key, value] of formData.entries()) {
if (json[key]) {
if (!Array.isArray(json[key])) {
json[key] = [json[key]];
}
json[key].push(value);
} else {
json[key] = value;
}
}
return json;
}设计思路
在处理表单数据时,有时需要将 FormData 对象转换为 JSON 对象,方便后续的处理和传输。
重点逻辑
FormData 条目:使用 entries 方法遍历 FormData 对象的所有键值对。formDataToStream.js该文件实现了一个 formDataToStream 函数,用于将 FormData 对象转换为可读流。
// formDataToStream.js
import { Readable } from 'stream';
export default function formDataToStream(formData) {
const parts = [];
for (const [key, value] of formData.entries()) {
parts.push(Buffer.from(`--${formData.boundary}\r\n`));
parts.push(Buffer.from(`Content-Disposition: form-data; name="${key}"`));
if (typeof value === 'object' && value instanceof File) {
parts.push(Buffer.from(`; filename="${value.name}"\r\n`));
parts.push(Buffer.from(`Content-Type: ${value.type || 'application/octet-stream'}\r\n\r\n`));
parts.push(value);
} else {
parts.push(Buffer.from('\r\n\r\n'));
parts.push(Buffer.from(value.toString()));
}
}
parts.push(Buffer.from(`--${formData.boundary}--\r\n`));
const readable = new Readable({
read() {
parts.forEach(part => this.push(part));
this.push(null);
}
});
return readable;
}设计思路
在发送包含文件上传的表单数据时,需要将 FormData 对象转换为可读流,以便通过 HTTP 请求发送。
重点逻辑
FormData 对象的键值对构建表单数据的各个部分。Readable 类创建一个可读流,并将构建好的表单数据添加到流中。HttpStatusCode.js该文件定义了一个包含常见 HTTP 状态码及其描述的对象。
// HttpStatusCode.js
export const HttpStatusCode = {
200: 'OK',
201: 'Created',
400: 'Bad Request',
401: 'Unauthorized',
403: 'Forbidden',
404: 'Not Found',
500: 'Internal Server Error'
// 其他状态码...
};设计思路
为了方便在代码中使用和理解 HTTP 状态码,将常见的状态码及其描述存储在一个对象中。
重点逻辑
isAbsoluteURL.js该文件实现了一个 isAbsoluteURL 函数,用于判断一个 URL 是否为绝对 URL。
// isAbsoluteURL.js
export default function isAbsoluteURL(url) {
return /^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(url);
}设计思路
在处理 URL 时,需要判断一个 URL 是否为绝对 URL,以便进行不同的处理。
重点逻辑
isAxiosError.js该文件实现了一个 isAxiosError 函数,用于判断一个错误对象是否为 Axios 错误。
// isAxiosError.js
export default function isAxiosError(error) {
return error && error.isAxiosError;
}设计思路
在处理错误时,需要判断一个错误对象是否为 Axios 错误,以便进行不同的处理。
重点逻辑
isAxiosError 属性:通过检查错误对象是否具有 isAxiosError 属性来判断是否为 Axios 错误。isURLSameOrigin.js该文件实现了一个 isURLSameOrigin 函数,用于判断两个 URL 是否具有相同的源。
// isURLSameOrigin.js
export default function isURLSameOrigin(url) {
const parsedUrl = new URL(url, window.location.href);
return parsedUrl.origin === window.location.origin;
}设计思路
在处理跨域请求时,需要判断两个 URL 是否具有相同的源,以确保请求的安全性。
重点逻辑
URL 对象:使用 URL 构造函数解析 URL,并比较其 origin 属性。parseHeaders.js该文件实现了一个 parseHeaders 函数,用于解析 HTTP 响应头。
// parseHeaders.js
export default function parseHeaders(headers) {
const parsed = {};
if (!headers) {
return parsed;
}
headers.split('\n').forEach(line => {
const index = line.indexOf(':');
if (index > 0) {
const key = line.slice(0, index).trim().toLowerCase();
const value = line.slice(index + 1).trim();
if (key) {
parsed[key] = parsed[key] ? `${parsed[key]}, ${value}` : value;
}
}
});
return parsed;
}设计思路
在处理 HTTP 响应时,需要解析响应头,将其转换为 JavaScript 对象,方便后续的处理。
重点逻辑
parseProtocol.js该文件实现了一个 parseProtocol 函数,用于解析 URL 的协议部分。
// parseProtocol.js
export default function parseProtocol(url) {
const match = url.match(/^([a-z][a-z\d\+\-\.]*:)/i);
return match ? match[1] : '';
}设计思路
在处理 URL 时,有时需要单独获取 URL 的协议部分,以便进行不同的处理。
重点逻辑
progressEventReducer.js该文件实现了一个 progressEventReducer 函数,用于处理进度事件。
// progressEventReducer.js
export default function progressEventReducer(state, event) {
return {
...state,
loaded: event.loaded,
total: event.total,
percent: event.total ? Math.round((event.loaded / event.total) * 100) : 0
};
}设计思路
在处理文件上传或下载时,需要实时获取进度信息。progressEventReducer 函数用于处理进度事件,更新进度状态。
重点逻辑
loaded 和 total 属性,更新进度状态对象。readBlob.js该文件实现了一个 readBlob 函数,用于读取 Blob 对象的内容。
// readBlob.js
export default function readBlob(blob) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsText(blob);
});
}设计思路
在处理文件上传或下载时,有时需要读取 Blob 对象的内容。readBlob 函数使用 FileReader 读取 Blob 对象的内容,并返回一个 Promise。
重点逻辑
FileReader:使用 FileReader 读取 Blob 对象的内容。onload 和 onerror 事件处理读取结果。resolveConfig.js该文件实现了一个 resolveConfig 函数,用于合并和解析 Axios 的配置对象。
// resolveConfig.js
import mergeConfig from './mergeConfig.js';
export default function resolveConfig(defaultConfig, userConfig) {
const config = mergeConfig(defaultConfig, userConfig);
// 处理其他配置项...
return config;
}设计思路
在使用 Axios 时,通常会有默认配置和用户配置。resolveConfig 函数用于合并这两个配置对象,并进行必要的解析和处理。
重点逻辑
mergeConfig 函数合并默认配置和用户配置。speedometer.js该文件实现了一个 speedometer 函数,用于计算数据传输速度。
// speedometer.js
export default function speedometer() {
let lastTime = Date.now();
let lastBytes = 0;
return function update(bytes) {
const now = Date.now();
const deltaTime = (now - lastTime) / 1000;
const deltaBytes = bytes - lastBytes;
const speed = deltaBytes / deltaTime;
lastTime = now;
lastBytes = bytes;
return speed;
};
}设计思路
在处理文件上传或下载时,需要实时计算数据传输速度。speedometer 函数通过记录上次的时间和字节数,计算两次之间的速度。
重点逻辑
spread.js该文件实现了一个 spread 函数,用于将一个数组展开为函数的参数。
// spread.js
export default function spread(callback) {
return function spreaded(arr) {
return callback(...arr);
};
}设计思路
在某些情况下,需要将一个数组的元素作为参数传递给一个函数。spread 函数可以方便地实现这个功能。
重点逻辑
... 将数组展开为函数的参数。throttle.js该文件实现了一个 throttle 函数,用于限制函数的调用频率。
// throttle.js
export default function throttle(fn, limit) {
let inThrottle;
return function throttled() {
const args = arguments;
const context = this;
if (!inThrottle) {
fn.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}设计思路
在处理高频事件时,如滚动事件、窗口调整事件等,需要限制函数的调用频率,以提高性能。
重点逻辑
inThrottle 来控制函数的调用频率。setTimeout 函数在一定时间后重置标志变量。toFormData.js该文件实现了一个 toFormData 函数,用于将一个普通对象转换为 FormData 对象。
// toFormData.js
export default function toFormData(data) {
const formData = new FormData();
for (const [key, value] of Object.entries(data)) {
formData.append(key, value);
}
return formData;
}设计思路
在处理表单数据时,有时需要将一个普通对象转换为 FormData 对象,方便通过 HTTP 请求发送。
重点逻辑
Object.entries 方法遍历对象的所有键值对。FormData:使用 append 方法将键值对添加到 FormData 对象中。toURLEncodedForm.js该文件实现了一个 toURLEncodedForm 函数,用于将一个普通对象转换为 URL 编码的表单数据。
// toURLEncodedForm.js
export default function toURLEncodedForm(data) {
const formData = new URLSearchParams();
for (const [key, value] of Object.entries(data)) {
formData.append(key, value);
}
return formData.toString();
}设计思路
在发送表单数据时,有时需要将数据转换为 URL 编码的格式。toURLEncodedForm 函数可以方便地实现这个功能。
重点逻辑
URLSearchParams:使用 URLSearchParams 类来处理 URL 编码。toString 方法将 URLSearchParams 对象转换为字符串。trackStream.js该文件实现了一个 trackStream 函数,用于跟踪流的进度。
// trackStream.js
export default function trackStream(stream, onProgress) {
let totalBytes = 0;
stream.on('data', chunk => {
totalBytes += chunk.length;
onProgress(totalBytes);
});
return stream;
}设计思路
在处理流数据时,需要实时跟踪流的进度。trackStream 函数通过监听流的 data 事件,统计已传输的字节数,并调用回调函数通知进度。
重点逻辑
data 事件:监听流的 data 事件,统计已传输的字节数。validator.js该文件实现了一些验证函数,用于验证数据的有效性。
// validator.js
export function isString(value) {
return typeof value === 'string';
}
export function isNumber(value) {
return typeof value === 'number';
}
// 其他验证函数...设计思路
在处理数据时,需要验证数据的有效性,以确保程序的正确性。这些验证函数提供了简单的 API 来验证数据的类型。
重点逻辑
typeof 运算符检查数据的类型。ZlibHeaderTransformStream.js该文件实现了一个 ZlibHeaderTransformStream 类,用于处理 Zlib 压缩数据的头部。
// ZlibHeaderTransformStream.js
class ZlibHeaderTransformStream extends TransformStream {
constructor() {
super({
start(controller) {
// 处理头部...
},
transform(chunk, controller) {
// 处理数据块...
controller.enqueue(chunk);
}
});
}
}
export default ZlibHeaderTransformStream;设计思路
在处理 Zlib 压缩数据时,需要处理压缩数据的头部。ZlibHeaderTransformStream 类通过继承 TransformStream,提供了一个可插拔的机制来处理头部。
重点逻辑
start 方法:在流开始时处理头部。transform 方法:在处理数据块时,将数据块加入到输出流中。本文深入剖析了 Axios 源码中的 helpers 工具库,对其中的 文件进行了详细解析。这些文件涵盖了从 URL 处理、数据转换、错误处理到进度跟踪等多个方面的功能,为 Axios 的核心功能提供了强大的支持。
通过阅读和解析 Axios 的源码,我们可以学习到许多优秀的设计模式和编程技巧。例如,使用自定义类扩展原生功能、使用闭包实现节流和防抖、使用 Promise 处理异步操作等。同时,我们也对 Axios 的内部实现有了更深入的了解,这有助于我们更好地使用 Axios 进行开发,并在遇到问题时能够更快地定位和解决。
总之,Axios 作为一个优秀的 HTTP 客户端库,其源码中蕴含着许多值得我们学习和借鉴的地方。通过深入阅读源码,我们可以不断提升自己的编程水平和解决问题的能力。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。