5月29日 22:54

What performance optimization techniques are there when using axios? How to reduce unnecessary network requests?

When using axios for HTTP requests, performance can be optimized in various ways to reduce unnecessary network overhead and improve user experience.

1. Request Caching

Memory Cache

javascript
class AxiosCache { constructor() { this.cache = new Map(); this.ttl = 5 * 60 * 1000; // 5 minute cache } 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 instance with cache const cachedApi = axios.create(); cachedApi.interceptors.request.use(config => { // Check cache const cached = cache.get(config); if (cached) { // Return cached data, cancel request config.adapter = () => Promise.resolve({ data: cached, status: 200, statusText: 'OK', headers: {}, config }); } return config; }); cachedApi.interceptors.response.use(response => { // Cache response data if (response.config.method === 'get') { cache.set(response.config, response.data); } return response; });

Using Cache API (Service Worker)

javascript
// Cache requests in 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. Request Deduplication (Debounce)

javascript
class 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); // If there's an ongoing identical request, return that Promise if (this.pendingRequests.has(key)) { return this.pendingRequests.get(key); } // Create new request const promise = axios(config).finally(() => { this.pendingRequests.delete(key); }); this.pendingRequests.set(key, promise); return promise; } } const deduper = new RequestDeduper(); // Usage const fetchUser = (id) => deduper.request({ method: 'GET', url: `/api/users/${id}` }); // Call multiple times simultaneously, only one request will be sent fetchUser(1); fetchUser(1); fetchUser(1); // Three calls, one request

3. Request Batching

javascript
class RequestBatcher { constructor() { this.batch = []; this.timeout = null; this.delay = 50; // Merge requests within 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 = []; // Merge requests const ids = currentBatch.map(item => item.request.id); try { const response = await axios.post('/api/batch', { ids }); // Distribute results currentBatch.forEach((item, index) => { item.resolve(response.data[index]); }); } catch (error) { currentBatch.forEach(item => { item.reject(error); }); } } }

4. Lazy Loading and Pagination

javascript
// Virtual scroll + pagination loading class VirtualListLoader { constructor(api, pageSize = 20) { this.api = api; this.pageSize = pageSize; this.cache = new Map(); this.loadingPages = new Set(); } async loadPage(page) { // Check cache if (this.cache.has(page)) { return this.cache.get(page); } // Prevent duplicate loading 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. Request Priority Management

javascript
class PriorityRequestQueue { constructor() { this.queue = []; this.maxConcurrent = 6; // Browser max concurrent requests 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(); } } } // Usage const queue = new PriorityRequestQueue(); // High priority request queue.add({ url: '/api/critical-data' }, 10); // Low priority request queue.add({ url: '/api/background-data' }, 1);

6. Compression and Request Minimization

javascript
// Request data compression const compressRequest = (data) => { // Remove undefined and null values const cleaned = JSON.parse(JSON.stringify(data)); return cleaned; }; // Field minimization const minimizeFields = (fields) => { // Only request needed fields return fields.join(','); }; axios.get('/api/users', { params: { fields: minimizeFields(['id', 'name', 'avatar']), include: minimizeFields(['posts', 'comments']) } });

7. Using HTTP/2 Server Push

javascript
// Server configuration for HTTP/2 Push // Add Link header in response // Link: </api/related-data>; rel=preload; as=fetch // Client preloading const preloadResources = () => { const links = document.querySelectorAll('link[rel=preload][as=fetch]'); links.forEach(link => { axios.get(link.href, { headers: { 'Purpose': 'prefetch' } }); }); };

8. Connection Reuse and Keep-Alive

javascript
// Use same axios instance to reuse connections const api = axios.create({ baseURL: 'https://api.example.com', // Enable keep-alive (in Node.js) httpAgent: new http.Agent({ keepAlive: true }), httpsAgent: new https.Agent({ keepAlive: true }) }); // Browser automatically reuses connections

9. Request Timeout Optimization

javascript
// Dynamically adjust timeout based on network conditions 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. Error Retry Strategy

javascript
axios.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; // Exponential backoff const backoff = Math.pow(2, config.retryCount) * 1000; await new Promise(resolve => setTimeout(resolve, backoff)); return axios(config); }); // Usage axios.get('/api/data', { retry: 3, retryDelay: 1000 });

11. Offline-First Strategy

javascript
// Use IndexedDB for caching const offlineFirstRequest = async (config) => { try { // Try network request first const response = await axios(config); // Cache to IndexedDB await saveToIndexedDB(config, response.data); return response; } catch (error) { // Network failed, try to read from cache const cached = await getFromIndexedDB(config); if (cached) { return { data: cached, fromCache: true }; } throw error; } };

12. Monitoring and Analytics

javascript
// Performance monitoring interceptor 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; // Report performance data analytics.track('api_request', { url: response.config.url, method: response.config.method, duration, status: response.status, size: JSON.stringify(response.data).length }); // Slow request warning if (duration > 3000) { console.warn(`Slow request: ${response.config.url} took ${duration}ms`); } return response; });

Best Practices Summary

Optimization StrategyApplicable ScenarioExpected Effect
Request CachingInfrequently changing dataReduce 50-90% requests
Request DeduplicationRapid consecutive triggersReduce duplicate requests
Request BatchingBatch operationsReduce request count
Pagination LoadingLong listsReduce initial load time
Priority QueueCritical/non-critical requestsImprove critical request response
Data CompressionLarge data transfersReduce transfer volume
Connection ReuseFrequent requestsReduce connection overhead
Smart TimeoutUnstable networksImprove user experience
Error RetryTemporary failuresImprove success rate
Offline-FirstWeak network environmentsImprove availability

标签:JavaScript前端Axios