乐闻世界logo
搜索文章和话题

axios 的版本更新历史中有哪些重要变化?如何处理不同版本的兼容性问题

3月6日 23:01

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.xv0.19.xv0.20.xv0.21.xv1.0.0+
CancelToken⚠️ 废弃
AbortController
Fetch 适配器
validateStatus✅ 改进
TypeScript⚠️⚠️✅ 改进
ESM 支持⚠️⚠️⚠️
安全修复

总结

  1. 版本选择:新项目建议使用 v1.6.0+,旧项目逐步迁移
  2. 兼容性处理:封装 axios 使用,隔离版本差异
  3. 升级策略:先测试后升级,使用锁定文件
  4. API 选择:优先使用新标准 (AbortController)
  5. 监控更新:关注安全更新和重大变更
标签:JavaScript前端Axios