Axios 从 0.x 版本发展到 1.x 版本,经历了多次重大更新,了解这些变化对于维护项目和升级非常重要。
1. 版本更新历史概览
主要版本里程碑
shell┌─────────────────────────────────────────────────────────────────┐ │ Axios 版本演进时间线 │ ├─────────────────────────────────────────────────────────────────┤ │ 2014 │ v0.1.0 │ 初始发布,基于 Promise 的 HTTP 客户端 │ │ 2015 │ v0.9.0 │ 添加拦截器功能 │ │ 2016 │ v0.12.0 │ 添加取消请求功能 (CancelToken) │ │ 2017 │ v0.16.0 │ 支持 async/await │ │ 2018 │ v0.18.0 │ 安全更新,修复 XSS 漏洞 │ │ 2019 │ v0.19.0 │ 改进错误处理,添加 validateStatus │ │ 2020 │ v0.20.0 │ 支持 TypeScript 类型改进 │ │ 2020 │ v0.21.0 │ 重大安全更新 │ │ 2022 │ v1.0.0 │ 正式发布 1.0 版本,Promise 化改进 │ │ 2023 │ v1.6.0 │ 支持 Fetch API 适配器 │ └─────────────────────────────────────────────────────────────────┘
2. 重要版本变更详解
v0.19.0 重大变更
javascript// v0.19.0 之前 - 错误处理 axios.get('/user') .catch(error => { // 404 错误也会进入 catch if (error.response) { // 服务器响应了错误状态码 } }); // v0.19.0 之后 - 引入 validateStatus axios.get('/user', { validateStatus: function (status) { // 默认:status >= 200 && status < 300 return status < 500; // 只有 500+ 才抛出错误 } }); // 自定义错误处理 const instance = axios.create({ validateStatus: (status) => { return status >= 200 && status < 300; // 默认值 } });
v0.20.0 TypeScript 改进
typescript// v0.20.0 之前的类型定义 import axios from 'axios'; // 类型定义不够完善 axios.get('/user').then(response => { // response.data 类型为 any }); // v0.20.0 之后的改进 import axios, { AxiosResponse } from 'axios'; interface User { id: number; name: string; } // 泛型支持 axios.get<User>('/user').then((response: AxiosResponse<User>) => { // response.data 类型为 User const user: User = response.data; }); // 请求配置类型 import { AxiosRequestConfig } from 'axios'; const config: AxiosRequestConfig = { url: '/user', method: 'get', headers: { 'Content-Type': 'application/json' } };
v1.0.0 重大变更
javascript// v1.0.0 之前 - CancelToken (已废弃) import axios from 'axios'; const CancelToken = axios.CancelToken; const source = CancelToken.source(); axios.get('/user', { cancelToken: source.token }); source.cancel('Operation canceled'); // v1.0.0 之后 - AbortController (推荐) const controller = new AbortController(); axios.get('/user', { signal: controller.signal }); controller.abort('Operation canceled'); // 兼容性处理 - 同时支持两种方式 function makeRequest(url, cancelTokenOrSignal) { const config = {}; if (cancelTokenOrSignal instanceof AbortSignal) { config.signal = cancelTokenOrSignal; } else { config.cancelToken = cancelTokenOrSignal; } return axios.get(url, config); }
v1.6.0 Fetch API 适配器
javascript// v1.6.0 引入 Fetch API 适配器 import axios from 'axios'; // 使用 Fetch API 适配器 const instance = axios.create({ adapter: 'fetch' // 或 require('axios/adapters/fetch') }); // 传统 XHR 适配器(默认) const xhrInstance = axios.create({ adapter: 'http' // 或 require('axios/adapters/xhr') }); // 条件选择适配器 const instance = axios.create({ adapter: typeof window !== 'undefined' && 'fetch' in window ? 'fetch' : 'xhr' });
3. 兼容性问题与解决方案
浏览器兼容性
javascript// 检查浏览器兼容性 function checkAxiosCompatibility() { const issues = []; // 检查 Promise 支持 if (typeof Promise === 'undefined') { issues.push('Promise not supported'); } // 检查 XMLHttpRequest if (typeof XMLHttpRequest === 'undefined') { issues.push('XMLHttpRequest not supported'); } // 检查 Fetch API (如果使用 fetch 适配器) if (typeof fetch === 'undefined') { issues.push('Fetch API not supported (optional)'); } return issues; } // Polyfill 方案 // 1. Promise Polyfill import 'es6-promise/auto'; // 2. Fetch API Polyfill import 'whatwg-fetch'; // 3. 完整兼容性处理 import axios from 'axios'; // 配置适配器回退 if (typeof XMLHttpRequest === 'undefined') { // Node.js 环境 axios.defaults.adapter = require('axios/lib/adapters/http'); }
Node.js 版本兼容性
javascript// package.json 中的引擎要求 { "engines": { "node": ">=12.0.0" } } // 运行时检查 const semver = require('semver'); const nodeVersion = process.version; if (!semver.satisfies(nodeVersion, '>=12.0.0')) { console.warn(`Axios requires Node.js >= 12.0.0, current: ${nodeVersion}`); } // 不同 Node 版本的适配 const http = require('http'); const https = require('https'); const instance = axios.create({ httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }), // Node.js 12+ 支持 maxBodyLength: Infinity, maxContentLength: Infinity });
版本检测与适配
javascript// axios-version-compat.js import axios from 'axios'; // 获取 axios 版本 const axiosVersion = axios.VERSION; // 版本比较工具 function compareVersions(v1, v2) { const parts1 = v1.split('.').map(Number); const parts2 = v2.split('.').map(Number); for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) { const p1 = parts1[i] || 0; const p2 = parts2[i] || 0; if (p1 > p2) return 1; if (p1 < p2) return -1; } return 0; } // 根据版本选择 API export function createCompatibleInstance(config = {}) { const isV1 = compareVersions(axiosVersion, '1.0.0') >= 0; const instance = axios.create({ ...config, // v1.0.0+ 的默认配置 ...(isV1 && { transitional: { clarifyTimeoutError: true } }) }); // 版本特定的拦截器 if (isV1) { // v1.x 的拦截器 instance.interceptors.request.use( (config) => { // v1.x 特有的处理 return config; }, (error) => Promise.reject(error) ); } else { // v0.x 的拦截器 instance.interceptors.request.use( (config) => config, (error) => Promise.reject(error) ); } return instance; } // 取消请求的兼容封装 export function createCancelToken() { const isV1 = compareVersions(axiosVersion, '1.0.0') >= 0; if (isV1) { // v1.x 使用 AbortController const controller = new AbortController(); return { token: controller.signal, cancel: (message) => controller.abort(message) }; } else { // v0.x 使用 CancelToken const source = axios.CancelToken.source(); return source; } }
4. 升级指南
从 v0.x 升级到 v1.x
javascript// 升级检查清单 const upgradeChecklist = { // 1. 检查 CancelToken 使用 checkCancelToken: () => { // 替换为 AbortController // 旧代码 const source = axios.CancelToken.source(); // 新代码 const controller = new AbortController(); }, // 2. 检查错误处理 checkErrorHandling: () => { // 确保 validateStatus 配置正确 axios.defaults.validateStatus = (status) => { return status >= 200 && status < 300; }; }, // 3. 检查 TypeScript 类型 checkTypeScript: () => { // 更新类型导入 // import { AxiosResponse } from 'axios'; }, // 4. 检查适配器配置 checkAdapter: () => { // 如果使用自定义适配器,需要更新 } }; // 自动升级脚本 function migrateAxiosCode(code) { // 替换 CancelToken code = code.replace( /axios\.CancelToken\.source\(\)/g, 'new AbortController()' ); // 替换 cancelToken 配置 code = code.replace( /cancelToken:\s*source\.token/g, 'signal: controller.signal' ); // 替换 cancel 调用 code = code.replace( /source\.cancel\(/g, 'controller.abort(' ); return code; }
依赖版本锁定
json// package.json - 锁定版本 { "dependencies": { "axios": "^1.6.0" }, "devDependencies": { "@types/axios": "^0.14.0" }, "resolutions": { "axios": "1.6.0" } } // package-lock.json / yarn.lock // 确保锁定文件提交到版本控制
5. 版本兼容性测试
javascript// __tests__/axios-compat.test.js import axios from 'axios'; describe('Axios Version Compatibility', () => { test('should have correct version', () => { expect(axios.VERSION).toBeDefined(); expect(axios.VERSION).toMatch(/^\d+\.\d+\.\d+/); }); test('should support AbortController in v1.x', () => { const [major] = axios.VERSION.split('.').map(Number); if (major >= 1) { const controller = new AbortController(); expect(() => { axios.get('/test', { signal: controller.signal }); }).not.toThrow(); } }); test('should support legacy CancelToken', () => { if (axios.CancelToken) { const source = axios.CancelToken.source(); expect(source.token).toBeDefined(); expect(typeof source.cancel).toBe('function'); } }); test('should handle errors consistently', async () => { try { await axios.get('http://invalid-domain-that-does-not-exist.com'); } catch (error) { expect(error).toBeDefined(); expect(error.message).toBeDefined(); } }); });
6. 最佳实践
版本管理策略
javascript// 1. 使用固定版本 // package.json { "dependencies": { "axios": "1.6.0" // 不使用 ^ 或 ~ } } // 2. 封装 axios,隔离版本影响 // api/client.js import axios from 'axios'; const apiClient = axios.create({ baseURL: process.env.API_URL, timeout: 10000, headers: { 'Content-Type': 'application/json' } }); // 封装请求方法,隐藏 axios 细节 export const api = { get: (url, config) => apiClient.get(url, config), post: (url, data, config) => apiClient.post(url, data, config), // ... 其他方法 }; // 3. 定期更新策略 // 创建更新脚本 scripts/update-axios.js const { execSync } = require('child_process'); const axios = require('axios/package.json'); console.log(`Current axios version: ${axios.version}`); // 检查最新版本 fetch('https://registry.npmjs.org/axios') .then(res => res.json()) .then(data => { const latest = data['dist-tags'].latest; console.log(`Latest axios version: ${latest}`); if (latest !== axios.version) { console.log('Update available. Run: npm update axios'); } });
兼容性配置模板
javascript// config/axios.js import axios from 'axios'; // 检测环境 const isBrowser = typeof window !== 'undefined'; const isNode = !isBrowser; const axiosVersion = axios.VERSION; // 基础配置 const baseConfig = { timeout: 10000, headers: { 'Content-Type': 'application/json' } }; // 环境特定配置 const envConfig = isNode ? { // Node.js 配置 httpAgent: new (require('http').Agent)({ keepAlive: true }), httpsAgent: new (require('https').Agent)({ keepAlive: true }) } : { // 浏览器配置 withCredentials: true, xsrfCookieName: 'XSRF-TOKEN', xsrfHeaderName: 'X-XSRF-TOKEN' }; // 版本特定配置 const versionConfig = axiosVersion.startsWith('1.') ? { // v1.x 配置 transitional: { clarifyTimeoutError: true, forcedJSONParsing: true } } : { // v0.x 配置 }; // 创建实例 const instance = axios.create({ ...baseConfig, ...envConfig, ...versionConfig }); export default instance;
版本兼容性速查表
| 特性 | v0.18.x | v0.19.x | v0.20.x | v0.21.x | v1.0.0+ |
|---|---|---|---|---|---|
| CancelToken | ✅ | ✅ | ✅ | ✅ | ⚠️ 废弃 |
| AbortController | ❌ | ❌ | ❌ | ❌ | ✅ |
| Fetch 适配器 | ❌ | ❌ | ❌ | ❌ | ✅ |
| validateStatus | ✅ | ✅ 改进 | ✅ | ✅ | ✅ |
| TypeScript | ⚠️ | ⚠️ | ✅ 改进 | ✅ | ✅ |
| ESM 支持 | ⚠️ | ⚠️ | ⚠️ | ✅ | ✅ |
| 安全修复 | ✅ | ✅ | ✅ | ✅ | ✅ |
总结
- 版本选择:新项目建议使用 v1.6.0+,旧项目逐步迁移
- 兼容性处理:封装 axios 使用,隔离版本差异
- 升级策略:先测试后升级,使用锁定文件
- API 选择:优先使用新标准 (AbortController)
- 监控更新:关注安全更新和重大变更