在使用 axios 进行 HTTP 请求时,可以通过多种方式优化性能,减少不必要的网络开销,提升用户体验。
1. 请求缓存
内存缓存
javascriptclass AxiosCache { constructor() { this.cache = new Map(); this.ttl = 5 * 60 * 1000; // 5分钟缓存 } generateKey(config) { return `${config.method}-${config.url}-${JSON.stringify(config.params)}`; } get(config) { const key = this.generateKey(config); const cached = this.cache.get(key); if (cached && Date.now() - cached.timestamp < this.ttl) { return cached.data; } this.cache.delete(key); return null; } set(config, data) { const key = this.generateKey(config); this.cache.set(key, { data, timestamp: Date.now() }); } clear() { this.cache.clear(); } } const cache = new AxiosCache(); // 使用缓存的 axios 实例 const cachedApi = axios.create(); cachedApi.interceptors.request.use(config => { // 检查缓存 const cached = cache.get(config); if (cached) { // 返回缓存数据,取消请求 config.adapter = () => Promise.resolve({ data: cached, status: 200, statusText: 'OK', headers: {}, config }); } return config; }); cachedApi.interceptors.response.use(response => { // 缓存响应数据 if (response.config.method === 'get') { cache.set(response.config, response.data); } return response; });
使用 Cache API(Service Worker)
javascript// 在 Service Worker 中缓存请求 self.addEventListener('fetch', event => { if (event.request.url.includes('/api/')) { event.respondWith( caches.match(event.request).then(response => { if (response) { return response; } return fetch(event.request).then(response => { const clone = response.clone(); caches.open('api-cache').then(cache => { cache.put(event.request, clone); }); return response; }); }) ); } });
2. 请求去重(防抖)
javascriptclass RequestDeduper { constructor() { this.pendingRequests = new Map(); } generateKey(config) { return `${config.method}-${config.url}-${JSON.stringify(config.params)}-${JSON.stringify(config.data)}`; } async request(config) { const key = this.generateKey(config); // 如果有正在进行的相同请求,返回该 Promise if (this.pendingRequests.has(key)) { return this.pendingRequests.get(key); } // 创建新请求 const promise = axios(config).finally(() => { this.pendingRequests.delete(key); }); this.pendingRequests.set(key, promise); return promise; } } const deduper = new RequestDeduper(); // 使用 const fetchUser = (id) => deduper.request({ method: 'GET', url: `/api/users/${id}` }); // 同时调用多次,只会发送一次请求 fetchUser(1); fetchUser(1); fetchUser(1); // 三次调用,一次请求
3. 请求合并
javascriptclass RequestBatcher { constructor() { this.batch = []; this.timeout = null; this.delay = 50; // 50ms 内的请求合并 } addRequest(request) { return new Promise((resolve, reject) => { this.batch.push({ request, resolve, reject }); clearTimeout(this.timeout); this.timeout = setTimeout(() => this.flush(), this.delay); }); } async flush() { if (this.batch.length === 0) return; const currentBatch = this.batch; this.batch = []; // 合并请求 const ids = currentBatch.map(item => item.request.id); try { const response = await axios.post('/api/batch', { ids }); // 分发结果 currentBatch.forEach((item, index) => { item.resolve(response.data[index]); }); } catch (error) { currentBatch.forEach(item => { item.reject(error); }); } } }
4. 懒加载和分页
javascript// 虚拟滚动 + 分页加载 class VirtualListLoader { constructor(api, pageSize = 20) { this.api = api; this.pageSize = pageSize; this.cache = new Map(); this.loadingPages = new Set(); } async loadPage(page) { // 检查缓存 if (this.cache.has(page)) { return this.cache.get(page); } // 防止重复加载 if (this.loadingPages.has(page)) { return new Promise(resolve => { const check = setInterval(() => { if (this.cache.has(page)) { clearInterval(check); resolve(this.cache.get(page)); } }, 100); }); } this.loadingPages.add(page); try { const response = await this.api.get('/api/items', { params: { page, pageSize: this.pageSize } }); this.cache.set(page, response.data); return response.data; } finally { this.loadingPages.delete(page); } } }
5. 请求优先级管理
javascriptclass PriorityRequestQueue { constructor() { this.queue = []; this.maxConcurrent = 6; // 浏览器最大并发数 this.running = 0; } add(config, priority = 0) { return new Promise((resolve, reject) => { this.queue.push({ config, priority, resolve, reject }); this.queue.sort((a, b) => b.priority - a.priority); this.process(); }); } async process() { if (this.running >= this.maxConcurrent || this.queue.length === 0) { return; } this.running++; const { config, resolve, reject } = this.queue.shift(); try { const response = await axios(config); resolve(response); } catch (error) { reject(error); } finally { this.running--; this.process(); } } } // 使用 const queue = new PriorityRequestQueue(); // 高优先级请求 queue.add({ url: '/api/critical-data' }, 10); // 低优先级请求 queue.add({ url: '/api/background-data' }, 1);
6. 压缩和精简请求
javascript// 请求数据压缩 const compressRequest = (data) => { // 移除 undefined 和 null 值 const cleaned = JSON.parse(JSON.stringify(data)); return cleaned; }; // 字段精简 const minimizeFields = (fields) => { // 只请求需要的字段 return fields.join(','); }; axios.get('/api/users', { params: { fields: minimizeFields(['id', 'name', 'avatar']), include: minimizeFields(['posts', 'comments']) } });
7. 使用 HTTP/2 Server Push
javascript// 服务端配置 HTTP/2 Push // 在响应头中添加 Link 头 // Link: </api/related-data>; rel=preload; as=fetch // 客户端预加载 const preloadResources = () => { const links = document.querySelectorAll('link[rel=preload][as=fetch]'); links.forEach(link => { axios.get(link.href, { headers: { 'Purpose': 'prefetch' } }); }); };
8. 连接复用和 Keep-Alive
javascript// 使用相同的 axios 实例以复用连接 const api = axios.create({ baseURL: 'https://api.example.com', // 启用 keep-alive(在 Node.js 中) httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }) }); // 浏览器端自动复用连接
9. 请求超时优化
javascript// 根据网络状况动态调整超时 const getTimeout = () => { const connection = navigator.connection; if (connection) { switch (connection.effectiveType) { case '4g': return 10000; case '3g': return 20000; case '2g': return 30000; default: return 15000; } } return 10000; }; axios.get('/api/data', { timeout: getTimeout() });
10. 错误重试策略
javascriptaxios.interceptors.response.use(null, async (error) => { const { config } = error; if (!config || !config.retry) { return Promise.reject(error); } config.retryCount = config.retryCount || 0; if (config.retryCount >= config.retry) { return Promise.reject(error); } config.retryCount += 1; // 指数退避 const backoff = Math.pow(2, config.retryCount) * 1000; await new Promise(resolve => setTimeout(resolve, backoff)); return axios(config); }); // 使用 axios.get('/api/data', { retry: 3, retryDelay: 1000 });
11. 离线优先策略
javascript// 使用 IndexedDB 缓存 const offlineFirstRequest = async (config) => { try { // 先尝试网络请求 const response = await axios(config); // 缓存到 IndexedDB await saveToIndexedDB(config, response.data); return response; } catch (error) { // 网络失败,尝试从缓存读取 const cached = await getFromIndexedDB(config); if (cached) { return { data: cached, fromCache: true }; } throw error; } };
12. 监控和分析
javascript// 性能监控拦截器 axios.interceptors.request.use(config => { config.metadata = { startTime: Date.now() }; return config; }); axios.interceptors.response.use(response => { const duration = Date.now() - response.config.metadata.startTime; // 上报性能数据 analytics.track('api_request', { url: response.config.url, method: response.config.method, duration, status: response.status, size: JSON.stringify(response.data).length }); // 慢请求警告 if (duration > 3000) { console.warn(`Slow request: ${response.config.url} took ${duration}ms`); } return response; });
最佳实践总结
| 优化策略 | 适用场景 | 预期效果 |
|---|---|---|
| 请求缓存 | 不频繁变化的数据 | 减少 50-90% 请求 |
| 请求去重 | 快速连续触发 | 减少重复请求 |
| 请求合并 | 批量操作 | 减少请求数量 |
| 分页加载 | 长列表 | 减少初始加载时间 |
| 优先级队列 | 关键/非关键请求 | 提升关键请求响应 |
| 数据压缩 | 大数据传输 | 减少传输体积 |
| 连接复用 | 频繁请求 | 减少连接开销 |
| 智能超时 | 不稳定网络 | 提升用户体验 |
| 错误重试 | 临时故障 | 提高成功率 |
| 离线优先 | 弱网环境 | 提升可用性 |