// 网络优化管理器 - 微信小程序专用 // 提供网络请求优化、缓存管理、离线支持等功能 const errorHandler = require('./error-handler.js'); const performanceMonitor = require('./performance-monitor.js'); /** * 网络优化管理器 * 功能: * 1. 请求缓存和优化 * 2. 离线数据支持 * 3. 网络状态监控 * 4. 请求队列管理 * 5. 断网重连机制 * 6. 数据压缩和优化 */ class NetworkOptimizer { constructor() { this.isInitialized = false; // 网络优化配置 this.config = { // 缓存配置 cache: { enabled: true, maxSize: 50 * 1024 * 1024, // 50MB defaultTTL: 300000, // 5分钟 maxAge: 24 * 60 * 60 * 1000, // 24小时 compressionEnabled: true }, // 离线配置 offline: { enabled: true, maxOfflineData: 10 * 1024 * 1024, // 10MB syncOnReconnect: true }, // 请求配置 request: { timeout: 10000, // 10秒超时 maxConcurrent: 6, // 最大并发请求数 retryAttempts: 3, // 重试次数 retryDelay: 1000, // 重试延迟 enableCompression: true, // 启用压缩 enableKeepAlive: true // 启用长连接 }, // 预加载配置 preload: { enabled: true, maxPreloadSize: 5 * 1024 * 1024, // 5MB preloadDelay: 2000 // 预加载延迟 } }; // 网络状态 this.networkState = { isOnline: true, networkType: 'unknown', lastOnlineTime: Date.now(), connectionQuality: 'good' }; // 缓存存储 this.cache = new Map(); this.cacheMetadata = new Map(); // 离线数据队列 this.offlineQueue = []; // 请求队列 this.requestQueue = []; this.activeRequests = new Set(); // 预加载队列 this.preloadQueue = []; // 统计数据 this.stats = { totalRequests: 0, cachedRequests: 0, failedRequests: 0, offlineRequests: 0, averageResponseTime: 0, cacheHitRate: 0 }; this.init(); } // 初始化网络优化器 async init() { if (this.isInitialized || !this.config.cache.enabled) return; try { // 获取网络状态 await this.updateNetworkState(); // 加载缓存数据 await this.loadCacheFromStorage(); // 设置网络监听 this.setupNetworkListeners(); // 启动请求队列处理 this.startRequestQueueProcessor(); // 启动缓存清理 this.startCacheCleanup(); // 启动预加载处理 this.startPreloadProcessor(); this.isInitialized = true; } catch (error) { console.error('❌ 网络优化器初始化失败:', error); } } // 🌐 ===== 网络状态管理 ===== // 更新网络状态 async updateNetworkState() { try { const networkInfo = await new Promise((resolve, reject) => { wx.getNetworkType({ success: resolve, fail: reject }); }); const wasOnline = this.networkState.isOnline; this.networkState.isOnline = networkInfo.networkType !== 'none'; this.networkState.networkType = networkInfo.networkType; if (this.networkState.isOnline) { this.networkState.lastOnlineTime = Date.now(); // 如果从离线恢复到在线,处理离线队列 if (!wasOnline && this.config.offline.syncOnReconnect) { this.processOfflineQueue(); } } // 更新连接质量 this.updateConnectionQuality(); } catch (error) { console.error('❌ 更新网络状态失败:', error); } } // 更新连接质量 updateConnectionQuality() { const networkType = this.networkState.networkType; if (networkType === 'wifi') { this.networkState.connectionQuality = 'excellent'; } else if (networkType === '4g') { this.networkState.connectionQuality = 'good'; } else if (networkType === '3g') { this.networkState.connectionQuality = 'fair'; } else if (networkType === '2g') { this.networkState.connectionQuality = 'poor'; } else { this.networkState.connectionQuality = 'unknown'; } } // 设置网络监听 setupNetworkListeners() { wx.onNetworkStatusChange((res) => { this.networkState.isOnline = res.isConnected; this.networkState.networkType = res.networkType; if (res.isConnected) { this.networkState.lastOnlineTime = Date.now(); // 网络恢复,处理离线队列 if (this.config.offline.syncOnReconnect) { this.processOfflineQueue(); } } this.updateConnectionQuality(); }); } // 📦 ===== 缓存管理 ===== // 获取缓存 getCache(key) { if (!this.config.cache.enabled) return null; const cached = this.cache.get(key); const metadata = this.cacheMetadata.get(key); if (!cached || !metadata) return null; // 检查缓存是否过期 if (Date.now() - metadata.timestamp > metadata.ttl) { this.cache.delete(key); this.cacheMetadata.delete(key); return null; } // 更新访问时间 metadata.lastAccess = Date.now(); metadata.accessCount++; this.stats.cachedRequests++; return cached; } // 设置缓存 setCache(key, data, ttl = this.config.cache.defaultTTL) { if (!this.config.cache.enabled) return; try { // 检查缓存大小 if (this.getCacheSize() > this.config.cache.maxSize) { this.cleanupCache(); } // 压缩数据(如果启用) const compressedData = this.config.cache.compressionEnabled ? this.compressData(data) : data; this.cache.set(key, compressedData); this.cacheMetadata.set(key, { timestamp: Date.now(), ttl: ttl, size: this.getDataSize(compressedData), lastAccess: Date.now(), accessCount: 1, compressed: this.config.cache.compressionEnabled }); // 异步保存到本地存储 this.saveCacheToStorage(); } catch (error) { console.error('❌ 设置缓存失败:', error); } } // 删除缓存 deleteCache(key) { this.cache.delete(key); this.cacheMetadata.delete(key); this.saveCacheToStorage(); } // 清空缓存 clearCache() { this.cache.clear(); this.cacheMetadata.clear(); wx.removeStorageSync('network_cache'); wx.removeStorageSync('cache_metadata'); } // 获取缓存大小 getCacheSize() { let totalSize = 0; for (const metadata of this.cacheMetadata.values()) { totalSize += metadata.size; } return totalSize; } // 清理过期缓存 cleanupCache() { const now = Date.now(); const keysToDelete = []; for (const [key, metadata] of this.cacheMetadata.entries()) { // 删除过期的缓存 if (now - metadata.timestamp > metadata.ttl || now - metadata.timestamp > this.config.cache.maxAge) { keysToDelete.push(key); } } // 如果还是太大,删除最少使用的缓存 if (this.getCacheSize() > this.config.cache.maxSize) { const sortedEntries = Array.from(this.cacheMetadata.entries()) .sort((a, b) => a[1].lastAccess - b[1].lastAccess); const halfSize = Math.floor(sortedEntries.length / 2); for (let i = 0; i < halfSize; i++) { keysToDelete.push(sortedEntries[i][0]); } } // 删除缓存 keysToDelete.forEach(key => { this.cache.delete(key); this.cacheMetadata.delete(key); }); if (keysToDelete.length > 0) { this.saveCacheToStorage(); } } // 启动缓存清理定时器 startCacheCleanup() { setInterval(() => { this.cleanupCache(); }, 5 * 60 * 1000); // 每5分钟清理一次 } // 📱 ===== 离线支持 ===== // 添加到离线队列 addToOfflineQueue(request) { if (!this.config.offline.enabled) return; // 检查离线数据大小 const currentSize = this.getOfflineQueueSize(); if (currentSize > this.config.offline.maxOfflineData) { // 删除最旧的请求 this.offlineQueue.shift(); } this.offlineQueue.push({ ...request, timestamp: Date.now(), retryCount: 0 }); this.stats.offlineRequests++; } // 处理离线队列 async processOfflineQueue() { if (!this.networkState.isOnline || this.offlineQueue.length === 0) return; const queue = [...this.offlineQueue]; this.offlineQueue = []; for (const request of queue) { try { await this.makeRequest(request); } catch (error) { console.error('❌ 离线请求同步失败:', request.url, error); // 重试次数未达到上限,重新加入队列 if (request.retryCount < this.config.request.retryAttempts) { request.retryCount++; this.offlineQueue.push(request); } } } } // 获取离线队列大小 getOfflineQueueSize() { return this.offlineQueue.reduce((size, request) => { return size + this.getDataSize(request); }, 0); } // 🚀 ===== 请求优化 ===== // 优化的请求方法 async optimizedRequest(options) { const requestId = performanceMonitor.startApiMonitoring(options.url, options.method); try { this.stats.totalRequests++; // 生成缓存键 const cacheKey = this.generateCacheKey(options); // 检查缓存 if (options.method === 'GET' || options.useCache) { const cached = this.getCache(cacheKey); if (cached) { performanceMonitor.endApiMonitoring(requestId, { success: true, statusCode: 200, fromCache: true }); return this.config.cache.compressionEnabled ? this.decompressData(cached) : cached; } } // 检查网络状态 if (!this.networkState.isOnline) { // 离线状态,添加到离线队列 if (options.method !== 'GET') { this.addToOfflineQueue(options); } throw new Error('网络不可用,请求已加入离线队列'); } // 检查并发限制 if (this.activeRequests.size >= this.config.request.maxConcurrent) { await this.waitForRequestSlot(); } // 发起请求 const result = await this.makeRequest(options); // 缓存GET请求结果 if (options.method === 'GET' || options.useCache) { this.setCache(cacheKey, result, options.cacheTTL); } performanceMonitor.endApiMonitoring(requestId, { success: true, statusCode: result.statusCode || 200 }); return result; } catch (error) { this.stats.failedRequests++; performanceMonitor.endApiMonitoring(requestId, { success: false, errorMessage: error.message }); // 使用错误处理器处理错误 throw await errorHandler.handleError(error, { url: options.url, method: options.method }); } } // 发起实际请求 async makeRequest(options) { const requestPromise = new Promise((resolve, reject) => { const requestOptions = { url: options.url, method: options.method || 'GET', data: options.data, header: { 'Content-Type': 'application/json', ...options.header }, timeout: options.timeout || this.config.request.timeout, success: (res) => { this.activeRequests.delete(requestPromise); resolve(res.data); }, fail: (error) => { this.activeRequests.delete(requestPromise); reject(new Error(error.errMsg || '请求失败')); } }; // 添加到活跃请求集合 this.activeRequests.add(requestPromise); wx.request(requestOptions); }); return requestPromise; } // 等待请求槽位 async waitForRequestSlot() { while (this.activeRequests.size >= this.config.request.maxConcurrent) { await new Promise(resolve => setTimeout(resolve, 100)); } } // 启动请求队列处理器 startRequestQueueProcessor() { setInterval(() => { this.processRequestQueue(); }, 1000); } // 处理请求队列 processRequestQueue() { while (this.requestQueue.length > 0 && this.activeRequests.size < this.config.request.maxConcurrent) { const request = this.requestQueue.shift(); this.optimizedRequest(request.options) .then(request.resolve) .catch(request.reject); } } // 🚀 ===== 预加载 ===== // 添加预加载请求 addPreloadRequest(url, options = {}) { if (!this.config.preload.enabled) return; this.preloadQueue.push({ url: url, options: options, priority: options.priority || 'normal', timestamp: Date.now() }); } // 启动预加载处理器 startPreloadProcessor() { setInterval(() => { this.processPreloadQueue(); }, this.config.preload.preloadDelay); } // 处理预加载队列 async processPreloadQueue() { if (!this.networkState.isOnline || this.preloadQueue.length === 0) return; // 按优先级排序 this.preloadQueue.sort((a, b) => { const priorityOrder = { high: 3, normal: 2, low: 1 }; return priorityOrder[b.priority] - priorityOrder[a.priority]; }); // 处理高优先级的预加载请求 const request = this.preloadQueue.shift(); try { await this.optimizedRequest({ url: request.url, method: 'GET', useCache: true, ...request.options }); } catch (error) { console.error('❌ 预加载失败:', request.url, error); } } // 🔧 ===== 工具方法 ===== // 生成缓存键 generateCacheKey(options) { const keyData = { url: options.url, method: options.method || 'GET', data: options.data || {} }; return `cache_${this.hashCode(JSON.stringify(keyData))}`; } // 简单哈希函数 hashCode(str) { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // 转换为32位整数 } return Math.abs(hash).toString(36); } // 压缩数据 compressData(data) { try { // 简单的JSON压缩(移除空格) return JSON.stringify(data); } catch (error) { return data; } } // 解压数据 decompressData(data) { try { return typeof data === 'string' ? JSON.parse(data) : data; } catch (error) { return data; } } // 获取数据大小 getDataSize(data) { try { return new Blob([JSON.stringify(data)]).size; } catch (error) { return JSON.stringify(data).length * 2; // 估算 } } // 保存缓存到本地存储 async saveCacheToStorage() { try { // 将Map转换为对象进行存储 const cacheData = Object.fromEntries(this.cache); const metadataData = Object.fromEntries(this.cacheMetadata); wx.setStorageSync('network_cache', cacheData); wx.setStorageSync('cache_metadata', metadataData); } catch (error) { console.error('❌ 保存缓存到本地存储失败:', error); } } // 从本地存储加载缓存 async loadCacheFromStorage() { try { const cacheData = wx.getStorageSync('network_cache'); const metadataData = wx.getStorageSync('cache_metadata'); if (cacheData) { this.cache = new Map(Object.entries(cacheData)); } if (metadataData) { this.cacheMetadata = new Map(Object.entries(metadataData)); } } catch (error) { console.error('❌ 从本地存储加载缓存失败:', error); } } // 获取网络统计 getNetworkStats() { this.stats.cacheHitRate = this.stats.totalRequests > 0 ? this.stats.cachedRequests / this.stats.totalRequests : 0; return { ...this.stats, networkState: this.networkState, cacheSize: this.getCacheSize(), offlineQueueSize: this.offlineQueue.length, activeRequests: this.activeRequests.size }; } // 销毁网络优化器 destroy() { // 保存缓存 this.saveCacheToStorage(); // 清理数据 this.cache.clear(); this.cacheMetadata.clear(); this.offlineQueue = []; this.requestQueue = []; this.activeRequests.clear(); this.preloadQueue = []; this.isInitialized = false; } } // 创建全局实例 const networkOptimizer = new NetworkOptimizer(); module.exports = networkOptimizer;