Axios源码笔记 | 深入解析 Core 核心处理引擎,从源码透视HTTP客户端设计哲学

举报
叶一一 发表于 2025/06/22 12:22:20 2025/06/22
【摘要】 一、Core 核心架构全景1.1 核心模块关系Axios核心引擎采用分层架构设计,各模块职责明确:控制中枢:Axios.js负责整体流程控制拦截系统:InterceptorManager实现请求/响应拦截请求调度:dispatchRequest处理实际请求派发错误处理:AxiosError统一错误格式数据处理:transformData处理数据转换配置管理:mergeConfig实现多级配置...

一、Core 核心架构全景

1.1 核心模块关系

Axios核心引擎采用分层架构设计,各模块职责明确:

  • 控制中枢:Axios.js负责整体流程控制
  • 拦截系统:InterceptorManager实现请求/响应拦截
  • 请求调度:dispatchRequest处理实际请求派发
  • 错误处理:AxiosError统一错误格式
  • 数据处理:transformData处理数据转换
  • 配置管理:mergeConfig实现多级配置合并

二、核心模块深度解析

2.1 Axios.js:控制中枢

2.1.1 核心类架构

class Axios {
  constructor(instanceConfig) {
    this.defaults = instanceConfig; // 全局默认配置
    this.interceptors = { // 拦截器管理系统
      request: new InterceptorManager(),
      response: new InterceptorManager()
    };
  }
}
  • 配置管理中心:维护defaults作为全局默认配置。
  • 拦截器容器:通过InterceptorManager管理请求/响应拦截器。

2.1.2 请求处理中枢

async request(configOrUrl, config) {
  try {
    return await this._request(...);
  } catch (err) {
    // 增强错误堆栈追踪
  }
}

_request() {
  // 处理参数重载(支持axios(url, config)语法)
  // 配置合并(mergeConfig)
  // 处理headers(使用AxiosHeaders)
  // 构建拦截器链
}
  • 多参数处理:支持axios(url, config)axios(config)两种调用方式
  • 错误处理增强:自动补充错误堆栈信息

2.1.3 拦截器执行机制

// 请求拦截器链
requestInterceptorChain.unshift(...)
// 响应拦截器链 
responseInterceptorChain.push(...)

// 异步执行流程
const chain = [dispatchRequest, undefined];
chain.unshift(...requestInterceptorChain);
chain.push(...responseInterceptorChain);
  • 请求拦截器:FILO(先进后出)顺序执行
  • 响应拦截器:FIFO(先进先出)顺序执行
  • 同步/异步模式:根据拦截器配置自动选择执行方式

2.1.4 HTTP方法适配器

// 无数据方法(GET等)
utils.forEach(['delete', 'get', 'head', 'options'], ...)

// 含数据方法(POST等)
utils.forEach(['post', 'put', 'patch'], function() {
  // 生成标准方法和Form表单方法
  function generateHTTPMethod(isForm) {...}
})
  • 自动生成方法:通过遍历生成所有HTTP方法
  • 表单处理:特殊处理xxxForm方法(自动设置Content-Type)

2.1.5 核心辅助功能

getUri(config) {
  // 构建完整URL
  buildFullPath(...);
  // 参数序列化
  buildURL(...);
}
  • URL构建:自动合并baseURL与请求URL
  • 参数序列化:支持自定义序列化函数

2.1.6 关键设计特点

  1. 配置分层管理:支持全局配置+请求级配置的深度合并。
  2. 异步流程控制:通过Promise链实现拦截器流水线。
  3. 扩展性设计:拦截器机制支持灵活的功能扩展。
  4. 语法兼容:同时支持现代async/await和传统Promise用法。

该文件作为Axios的核心控制器,协调了配置管理、请求派发、拦截器流水线、方法适配等关键流程,体现了中间件架构的设计思想。

2.2 InterceptorManager.js:拦截器系统

2.2.1 数据结构设计

class InterceptorManager {
  constructor() {
    this.handlers = []; // 拦截器存储容器
  }
}
  • 环形队列结构:使用数组存储拦截器对象。
  • 空位标记:被移除的拦截器置为 null(非物理删除)。

2.2.2 注册拦截器 (use)

use(fulfilled, rejected, options) {
  this.handlers.push({
    fulfilled,       // 成功回调
    rejected,        // 失败回调
    synchronous: options?.synchronous, // 同步模式标识
    runWhen: options?.runWhen          // 执行条件判断
  });
  return this.handlers.length - 1; // 返回索引作为ID
}
  • 支持链式配置:返回拦截器ID用于后续管理。
  • 条件执行:通过 runWhen 实现动态拦截控制。

2.2.3 移除拦截器 (eject)

eject(id) {
  if (this.handlers[id]) {
    this.handlers[id] = null; // 标记删除而非物理删除
  }
}
  • 逻辑删除机制:保持数组索引稳定性。
  • 内存优化:避免频繁数组操作影响性能。

2.2.4 遍历机制 (forEach)

forEach(fn) {
  utils.forEach(this.handlers, h => {
    if (h !== null) fn(h); // 自动跳过已移除的拦截器
  });
}
  • 安全遍历:自动过滤 null 值项。
  • 执行效率:O(n) 时间复杂度遍历。

2.2.5 拦截器对象结构

{
  fulfilled: (config) => {},    // 成功处理函数
  rejected: (error) => {},      // 错误处理函数
  synchronous: false,           // 是否同步执行
  runWhen: (config) => true     // 执行条件判断函数
}

2.2.6 执行流程控制

当与 Axios 核心配合时:

  1. 请求拦截器:逆序执行(后添加的先执行)。
  2. 响应拦截器:正序执行(先添加的先执行)。
  3. 条件执行:通过 runWhen 判断是否执行拦截器。
// Axios.js 中的处理逻辑示例
this.interceptors.request.forEach(interceptor => {
  if (interceptor.runWhen(config) === false) return;
  // 加入执行链...
});

2.2.7 关键设计特点

  1. 松耦合设计:拦截器与核心逻辑解耦。
  2. 灵活控制:支持同步/异步两种执行模式。
  3. 动态管理:运行时增删改查拦截器。
  4. 条件过滤:通过 runWhen 实现智能拦截。

2.3 dispatchRequest.js:请求调度中心

2.3.1 预处理阶段

export default function dispatchRequest(config) {
  throwIfCancellationRequested(config); // 前置取消检查
  config.headers = AxiosHeaders.from(config.headers); // 头部标准化
  
  // 请求数据转换
  config.data = transformData.call(config, config.transformRequest);
  
  // 自动设置 Content-Type
  if (['post', 'put', 'patch'].includes(config.method)) {
    config.headers.setContentType('application/x-www-form-urlencoded', false);
  }
}
  • 取消安全检查:在请求发起前进行双重取消检查(CancelToken 和 AbortSignal)。
  • 头部标准化:将 headers 转换为 AxiosHeaders 实例。
  • 智能 Content-Type:为常见数据提交方法设置默认类型(不覆盖用户自定义值)。

2.3.2 适配器调度

const adapter = adapters.getAdapter(config.adapter || defaults.adapter);
return adapter(config).then(...)
  • 环境自适应:自动选择 XHR(浏览器)或 HTTP(Node.js)适配器。
  • 扩展支持:允许自定义适配器实现特殊网络协议。

2.3.3 数据转换流水线

// 请求数据转换
transformData.call(config, config.transformRequest)

// 响应数据转换
response.data = transformData.call(config, config.transformResponse, response)
  • 双向转换:支持请求/响应数据的自定义转换管道。
  • 默认处理:包含 JSON 解析、URL 编码等基础转换逻辑

2.3.4 异常处理机制

function onAdapterRejection(reason) {
  if (!isCancel(reason)) {
    // 处理非取消类错误
    if (reason.response) {
      reason.response.data = transformData(...)
    }
  }
  return Promise.reject(reason);
}
  • 错误分类:区分取消错误与真实网络错误。
  • 错误数据转换:对非常规响应(如 4xx/5xx)进行数据转换。

2.3.5 关键设计特点

1、取消感知设计

function throwIfCancellationRequested(config) {
  if (config.cancelToken) { /* Check CancelToken */ }
  if (config.signal?.aborted) { /* Check AbortSignal */ }
}
  • 同时支持新旧两种取消方案。
  • 在请求发起前和响应接收后进行双重检查。

2、数据转换管道

// 典型转换流程示例
请求数据 -> transformRequest 数组 -> 网络适配器
响应数据 -> transformResponse 数组 -> 用户接收

3、适配器抽象层

// 适配器注册机制示例
adapters.register('custom', (config) => {
  // 自定义网络实现
})

2.3.6 核心价值体现

  • 统一入口:聚合所有预处理逻辑。
  • 异常隔离:确保转换错误不会导致进程崩溃。
  • 扩展基点:通过适配器机制支持多种网络协议。
  • 性能优化:避免不必要的转换操作。

该模块作为 Axios 网络通信的最后一道关口,在确保安全性的同时,为上层提供了统一的网络抽象接口。

三、关键支持模块解析

3.1 AxiosError.js:错误处理系统

3.1.1 错误类继承体系

class AxiosError extends Error {
  constructor(message, code, config, request, response) {
    super(message);
    // 保留完整错误堆栈
    Error.captureStackTrace(this, this.constructor);
    // 扩展属性
    this.config = config;
    this.request = request;
    this.response = response;
    this.code = code; // 标准化错误码
  }
}
  • Error 继承:完整保留原生错误特性。
  • 上下文保留:携带请求配置、请求实例、响应对象。
  • 状态码双保险codestatus 并存(兼容不同错误来源)。

3.1.2 标准化错误码

Object.defineProperties(AxiosError, {
  ECONNABORTED: {value: 'ECONNABORTED'}, // 连接中止
  ETIMEDOUT: {value: 'ETIMEDOUT'},       // 超时错误
  ERR_CANCELED: {value: 'ERR_CANCELED'}, // 手动取消
  // ...其他 10+ 标准错误码
});
  • 跨环境统一:浏览器/Node.js 共用同一套错误标识。
  • 错误分类:包含网络错误、配置错误、响应错误等类型。

3.1.3 错误特征标识

Object.defineProperty(AxiosError.prototype, 'isAxiosError', {
  value: true // 快速识别标志
});
  • 类型检测:替代 instanceof 检查,避免多实例问题。
  • 兼容性增强:应对不同打包环境下的类继承问题。

3.1.4 错误序列化

toJSON() {
  return {
    message: this.message,
    config: utils.toJSONObject(this.config), // 安全序列化
    code: this.code,
    status: this.status
  };
}
  • 安全转换:过滤循环引用等危险结构。
  • 关键信息保留:包含调试所需的最小数据集。

3.1.5 错误包装机制

static from(error, code, config, request, response) {
  const axiosError = Object.create(this.prototype);
  // 保留原始错误信息
  axiosError.cause = error; // Error Cause 标准提案实现
  return axiosError;
}
  • 错误链支持:通过 cause 属性保留原始错误。
  • 无缝转换:将第三方库错误转换为 Axios 标准格式。

3.1.6 设计亮点

1、上下文完整性

try {/* 请求 */} 
catch (err) {
  console.log(err.config.url); // 可追溯出错请求
  console.log(err.response.status); // 查看服务端响应
}

2、错误码标准化

if (err.code === AxiosError.ECONNABORTED) {
  // 处理连接中止
}

3、安全序列化

// 日志记录时自动过滤敏感信息
JSON.stringify(err)

4、错误溯源

console.log(err.cause); // 查看底层系统错误

该错误系统通过标准化、结构化、可追溯的设计,显著提升了异步请求的调试效率和错误处理能力。

3.2 mergeConfig.js:配置合并策略

3.2.1 合并策略矩阵

const mergeMap = {
  url: valueFromConfig2,          // 完全使用新配置
  method: valueFromConfig2,
  data: valueFromConfig2,
  baseURL: defaultToConfig2,      // 新配置优先
  headers: 深度合并策略,           // 特殊对象合并
  validateStatus: mergeDirectKeys // 条件合并
  // ...其他 30+ 配置项策略
};

3.2.2 核心合并策略类型

1、新配置优先 (valueFromConfig2)

function valueFromConfig2(_, b) {
  return b !== undefined ? clone(b) : undefined;
}
  • 适用场景:URL、请求方法等需完全覆盖的配置。
  • 典型配置项
    • url
    • method
    • data

2、默认值优先 (defaultToConfig2)

function defaultToConfig2(a, b) {
  return b !== undefined ? clone(b) : clone(a);
}
  • 适用场景:基础配置需要继承默认值。
  • 典型配置项
    • baseURL
    • timeout
    • withCredentials

3、深度合并 (mergeDeepProperties)

function mergeDeepProperties(a, b) {
  if (均为对象) return 深度合并;
  if (b是数组) return 复制新数组;
  return b ?? a;
}
  • 适用场景:复杂对象结构配置。
  • 典型配置项
    • headers(特殊处理)
    • transformRequest
    • transformResponse

4、条件合并 (mergeDirectKeys)

function mergeDirectKeys(a, b) {
  return config2存在该属性 ? 合并值 : 保留config1值;
}
  • 适用场景:需要条件判断的配置。
  • 典型配置项
    • validateStatus

3.2.3 Headers 特殊处理

headers: (a, b) => mergeDeepProperties(
  headersToObject(a), 
  headersToObject(b),
  true // 启用大小写不敏感合并
)
  • 转换机制:将 AxiosHeaders 实例转为普通对象。
  • 合并特点
    • 保留双方特有 headers。
    • 同名 header 新配置覆盖旧值。
    • 自动处理大小写(如 Content-Typecontent-type)。

3.2.5 关键设计原则

1、不变性原则

function clone(source) {
  if (数组) return source.slice();
  if (对象) return {...source};
  return source;
}
    • 始终保持原始配置不可变。

2、安全递归合并

utils.merge 实现:
- 跳过原型链属性
- 处理循环引用
- 控制合并深度(默认 10 层)

3、策略扩展性

// 自定义合并策略示例
mergeMap.customProp = (a, b) => {
  return a + b; // 自定义合并逻辑
}

四、高级特性实现

4.1 transformData.js:数据转换引擎

该模块是 Axios 数据转换的核心处理器,负责请求/响应数据的多阶段转换流程。

4.1.1 转换流程架构

export default function transformData(fns, response) {
  const config = this || defaults; // 获取当前配置
  const context = response || config; // 确定执行上下文
  const headers = AxiosHeaders.from(context.headers); // 标准化请求头
  let data = context.data; // 原始数据

  // 执行转换流水线
  utils.forEach(fns, function(fn) {
    data = fn(data, headers.normalize(), response?.status);
  });

  return data;
}

4.1.2 转换函数执行特点

1、链式传递

// 示例转换函数队列
const transformRequest = [
  data => JSON.stringify(data),    // 序列化
  data => encryptPayload(data)     // 加密
];

// 执行顺序:序列化 -> 加密

2、上下文绑定

fn.call(config, data, headers, status)
    • this 指向当前请求配置。
    • 可访问完整的配置参数。

3、头信息交互

headers.normalize() // 标准化头信息键名(如 content-type -> Content-Type)
    • 每次转换前更新头信息状态。
    • 允许转换函数修改头信息(如根据数据类型设置 Content-Type)。

4.1.3 双模式工作机制

1、请求阶段转换:

// 请求处理流程
config.transformRequest.forEach(fn => {
  data = fn(data, headers);
});
  • 典型处理
    • JSON 序列化。
    • URL 参数编码。
    • FormData 转换。

2、响应阶段转换:

// 响应处理流程
config.transformResponse.forEach(fn => {
  data = fn(data, headers, status);
});
  • 典型处理
    • JSON 解析。
    • 数据解密。
    • 错误格式标准化。

4.1.4 转换函数签名规范

type TransformFunction = (
  data: any,
  headers: AxiosHeaders,
  status?: number
) => any;
  • 输入:原始数据 + 头信息 + HTTP 状态码(仅响应阶段)。
  • 输出:处理后的新数据。

4.1.5 关键设计特点

1、不可变处理

let data = context.data; // 保留原始数据引用
data = fn(data); // 每次转换创建新数据
    • 确保原始数据不被意外修改。

2、状态感知转换

// 根据 HTTP 状态码进行不同处理
function statusAwareTransform(data, headers, status) {
  if (status >= 400) {
    return { error: data };
  }
  return data;
}

3、头信息联动

function setContentType(data, headers) {
  if (typeof data === 'string') {
    headers.set('Content-Type', 'text/plain');
  }
  return data;
}

4.2 settle.js:响应状态处理

4.2.1 验证条件判断

const validateStatus = response.config.validateStatus;
if (!response.status || !validateStatus || validateStatus(response.status)) {
    resolve(response);
}

三层短路逻辑

  • 无状态码时直接成功(异常情况)。
  • 没有配置验证函数时默认成功。
  • 有验证函数时以函数结果为准。

4.2.2 错误处理机制

reject(new AxiosError(
    'Request failed with status code ' + response.status,
    [/*...*/][Math.floor(response.status/100)-4], // 错误类型选择器
    response.config,
    response.request,
    response
));
  • 错误类型动态判断
    • 4xx 状态码 ➔ ERR_BAD_REQUEST(客户端错误)。
    • 5xx 状态码 ➔ ERR_BAD_RESPONSE(服务端错误)
  • 错误对象包含:配置信息、请求实例、响应对象,便于调试。

4.2.3 设计特点

  • 灵活的状态验证:允许通过 validateStatus 配置自定义状态码验证逻辑。
  • 错误分类明确:通过状态码百位数自动区分错误类型。
  • 完整上下文保留:错误对象携带完整的请求/响应上下文信息。

五、结语

通过对 Core 核心引擎的深度剖析,我们揭示了其分层架构、拦截器链、数据管道三大设计支柱。从网页的配置合并到网页的拦截器机制,每个模块都体现了高内聚低耦合的设计哲学。

收获

  1. 拦截器系统采用Promise链实现中间件模式。
  2. 配置管理系统通过原型链继承实现灵活覆盖。
  3. 适配器模式完美解决跨环境兼容问题。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。