Axios 是一个基于 Promise 的 HTTP 客户端,其核心架构设计优雅,支持浏览器和 Node.js 环境。
1. 核心架构概览
整体架构图
shell┌─────────────────────────────────────────────────────────────┐ │ Axios 入口 │ │ axios.create() / axios.get() / axios.post() │ └──────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Axios 实例 │ │ - defaults (默认配置) │ │ - interceptors (拦截器) │ │ - request / get / post 等方法 │ └──────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 请求处理流程 │ │ 1. 合并配置 (mergeConfig) │ │ 2. 请求拦截器 (request interceptors) │ │ 3. 转换请求数据 (transformRequest) │ │ 4. 适配器执行请求 (adapter) │ │ 5. 转换响应数据 (transformResponse) │ │ 6. 响应拦截器 (response interceptors) │ └──────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 适配器层 (Adapter) │ │ 浏览器: XMLHttpRequest │ │ Node.js: http / https 模块 │ └─────────────────────────────────────────────────────────────┘
2. 核心源码解析
Axios 类定义
javascript// 简化版 Axios 类结构 class Axios { constructor(instanceConfig) { // 默认配置 this.defaults = instanceConfig; // 拦截器管理器 this.interceptors = { request: new InterceptorManager(), response: new InterceptorManager() }; } // 核心请求方法 request(config) { // 1. 配置处理 if (typeof config === 'string') { config = arguments[1] || {}; config.url = arguments[0]; } // 2. 合并配置 config = mergeConfig(this.defaults, config); // 3. 设置请求方法 if (config.method) { config.method = config.method.toLowerCase(); } // 4. 构建拦截器链 const chain = [dispatchRequest, undefined]; let promise = Promise.resolve(config); // 请求拦截器 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); }); // 5. 执行链 while (chain.length) { promise = promise.then(chain.shift(), chain.shift()); } return promise; } // 便捷方法 get(url, config) { return this.request(mergeConfig(config || {}, { method: 'get', url })); } post(url, data, config) { return this.request(mergeConfig(config || {}, { method: 'post', url, data })); } // ... delete, head, options, put, patch }
拦截器管理器
javascript// 拦截器管理器实现 class InterceptorManager { constructor() { this.handlers = []; } // 添加拦截器 use(fulfilled, rejected, options) { this.handlers.push({ fulfilled, rejected, synchronous: options?.synchronous || false, runWhen: options?.runWhen || null }); return this.handlers.length - 1; } // 移除拦截器 eject(id) { if (this.handlers[id]) { this.handlers[id] = null; } } // 遍历拦截器 forEach(fn) { this.handlers.forEach((handler) => { if (handler !== null) { fn(handler); } }); } }
请求分发器
javascript// 请求分发函数 function dispatchRequest(config) { // 1. 检查请求是否已取消 throwIfCancellationRequested(config); // 2. 转换请求数据 config.data = transformData( config.data, config.headers, config.transformRequest ); // 3. 合并 headers config.headers = flattenHeaders( config.headers, config.method ); // 4. 获取适配器 const adapter = config.adapter || defaults.adapter; // 5. 执行适配器 return adapter(config).then( function onAdapterResolution(response) { // 检查请求是否已取消 throwIfCancellationRequested(config); // 转换响应数据 response.data = transformData( response.data, response.headers, config.transformResponse ); return response; }, function onAdapterRejection(reason) { if (!isCancel(reason)) { throwIfCancellationRequested(config); // 转换错误信息 if (reason && reason.response) { reason.response.data = transformData( reason.response.data, reason.response.headers, config.transformResponse ); } } return Promise.reject(reason); } ); }
3. 适配器层实现
浏览器适配器 (XHR)
javascript// 浏览器端适配器 - 基于 XMLHttpRequest function xhrAdapter(config) { return new Promise(function dispatchXhrRequest(resolve, reject) { const requestData = config.data; const requestHeaders = config.headers; const responseType = config.responseType; // 创建 XHR 对象 const request = new XMLHttpRequest(); // 配置请求 request.open(config.method.toUpperCase(), buildURL(config.url, config.params), true); // 设置超时 request.timeout = config.timeout; // 设置响应类型 if (responseType) { request.responseType = responseType; } // 设置 withCredentials if (config.withCredentials) { request.withCredentials = true; } // 设置请求头 Object.keys(requestHeaders).forEach((key) => { if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') { delete requestHeaders[key]; } else { request.setRequestHeader(key, requestHeaders[key]); } }); // 处理取消请求 if (config.cancelToken) { config.cancelToken.promise.then(function onCanceled(cancel) { request.abort(); reject(cancel); }); } // 监听状态变化 request.onreadystatechange = function handleLoad() { if (!request || request.readyState !== 4) { return; } // 准备响应对象 const response = { data: responseType === 'text' ? request.responseText : request.response, status: request.status, statusText: request.statusText, headers: parseHeaders(request.getAllResponseHeaders()), config, request }; // 根据状态码处理响应 settle(resolve, reject, response); }; // 处理错误 request.onerror = function handleError() { reject(new Error('Network Error')); }; // 处理超时 request.ontimeout = function handleTimeout() { reject(new Error(`timeout of ${config.timeout}ms exceeded`)); }; // 处理下载进度 if (config.onDownloadProgress) { request.onprogress = config.onDownloadProgress; } // 处理上传进度 if (config.onUploadProgress) { request.upload.onprogress = config.onUploadProgress; } // 发送请求 request.send(requestData); }); }
Node.js 适配器 (HTTP)
javascript// Node.js 端适配器 - 基于 http/https 模块 function httpAdapter(config) { return new Promise(function dispatchHttpRequest(resolve, reject) { const { url, method, data, headers, timeout, httpAgent, httpsAgent } = config; // 解析 URL const parsed = new URL(url); const isHttps = parsed.protocol === 'https:'; // 选择模块 const http = isHttps ? require('https') : require('http'); // 配置选项 const options = { hostname: parsed.hostname, port: parsed.port, path: parsed.pathname + parsed.search, method: method.toUpperCase(), headers, agent: isHttps ? httpsAgent : httpAgent, timeout }; // 创建请求 const req = http.request(options, (res) => { let responseData = []; res.on('data', (chunk) => { responseData.push(chunk); }); res.on('end', () => { const response = { data: Buffer.concat(responseData).toString(), status: res.statusCode, statusText: res.statusMessage, headers: res.headers, config }; settle(resolve, reject, response); }); }); // 处理错误 req.on('error', (err) => { reject(err); }); // 处理超时 req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); }); // 发送数据 if (data) { req.write(data); } req.end(); }); }
4. 请求流程详解
完整请求流程
javascript// 示例:axios.get('/user') // Step 1: 调用 axios.get axios.get('/user', { params: { id: 123 } }); // Step 2: 进入 Axios.request 方法 // - 合并配置 // - 构建拦截器链 // Step 3: 执行请求拦截器 // 拦截器 2 (fulfilled) → 拦截器 1 (fulfilled) → dispatchRequest // Step 4: dispatchRequest // - 转换请求数据 // - 获取适配器 // - 执行适配器 // Step 5: 适配器执行 // 浏览器: 创建 XHR → 配置 → 发送 → 监听回调 // Node.js: 创建 HTTP 请求 → 配置 → 发送 → 监听回调 // Step 6: 处理响应 // - 转换响应数据 // - 执行响应拦截器 // - 返回结果 // 完整流程代码示例 axios.get('/user', { params: { id: 123 } }) .then(response => { // 响应拦截器处理后的结果 console.log(response.data); }) .catch(error => { // 错误处理 console.error(error); });
拦截器执行顺序
javascript// 请求拦截器执行顺序:后添加的先执行 axios.interceptors.request.use(config => { console.log('Request Interceptor 1'); return config; }); axios.interceptors.request.use(config => { console.log('Request Interceptor 2'); return config; }); // 执行顺序: Interceptor 2 → Interceptor 1 → Request // 响应拦截器执行顺序:先添加的先执行 axios.interceptors.response.use(response => { console.log('Response Interceptor 1'); return response; }); axios.interceptors.response.use(response => { console.log('Response Interceptor 2'); return response; }); // 执行顺序: Response → Interceptor 1 → Interceptor 2
5. 取消请求实现原理
CancelToken 实现
javascript// CancelToken 类 class CancelToken { constructor(executor) { let resolvePromise; // 创建 Promise this.promise = new Promise((resolve) => { resolvePromise = resolve; }); // 执行器函数 executor((message) => { if (this.reason) { return; // 已经取消过 } this.reason = new Cancel(message); resolvePromise(this.reason); }); } // 静态方法:source static source() { let cancel; const token = new CancelToken((c) => { cancel = c; }); return { token, cancel }; } } // Cancel 类 class Cancel { constructor(message) { this.message = message; } toString() { return `Cancel${this.message ? ': ' + this.message : ''}`; } } // 使用示例 const source = CancelToken.source(); axios.get('/user', { cancelToken: source.token }); // 取消请求 source.cancel('Operation canceled by the user.');
6. 数据转换流程
javascript// 请求数据转换 function transformRequest(data, headers) { // 如果是对象,转换为 JSON 字符串 if (typeof data === 'object' && data !== null) { headers['Content-Type'] = 'application/json'; return JSON.stringify(data); } return data; } // 响应数据转换 function transformResponse(data) { // 尝试解析 JSON if (typeof data === 'string') { try { data = JSON.parse(data); } catch (e) { // 不是 JSON,保持原样 } } return data; } // 在配置中使用 transformRequest: [ function(data, headers) { // 自定义转换逻辑 return data; } ], transformResponse: [ function(data) { // 自定义转换逻辑 return data; } ]
7. 配置合并策略
javascript// 配置合并函数 function mergeConfig(config1, config2) { const config = {}; // 默认配置 const defaultToConfig2 = [ 'url', 'method', 'data', 'baseURL', 'transformRequest', 'transformResponse', 'paramsSerializer', 'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName', 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress', 'maxContentLength', 'validateStatus', 'maxRedirects', 'httpAgent', 'httpsAgent', 'cancelToken', 'socketPath' ]; defaultToConfig2.forEach(prop => { if (typeof config2[prop] !== 'undefined') { config[prop] = config2[prop]; } else if (typeof config1[prop] !== 'undefined') { config[prop] = config1[prop]; } }); // Headers 需要深度合并 config.headers = mergeHeaders(config1.headers, config2.headers); // Params 需要合并 config.params = { ...config1.params, ...config2.params }; return config; }
8. 核心设计模式
1. 适配器模式 (Adapter)
javascript// 统一接口,不同实现 const adapter = isBrowser ? xhrAdapter : httpAdapter;
2. 责任链模式 (Chain of Responsibility)
javascript// 拦截器链 const chain = [ requestInterceptor2, undefined, requestInterceptor1, undefined, dispatchRequest, undefined, responseInterceptor1, undefined, responseInterceptor2, undefined ];
3. 工厂模式 (Factory)
javascript// 创建 Axios 实例 axios.create = function create(instanceConfig) { return new Axios(instanceConfig); };
4. 观察者模式 (Observer)
javascript// 取消令牌 const source = CancelToken.source(); source.token.promise.then(onCanceled);
总结
Axios 的核心设计亮点:
- 统一的 Promise API:简化异步处理
- 拦截器机制:灵活的请求/响应处理
- 适配器模式:支持多平台运行
- 配置合并策略:灵活的配置管理
- 取消请求:完善的请求控制
- 数据转换:自动化的数据处理
- 错误处理:统一的错误管理机制