findme-miniprogram-frontend/utils/network-optimizer.js

671 lines
16 KiB
JavaScript
Raw Normal View History

2025-12-27 17:16:03 +08:00
// 网络优化管理器 - 微信小程序专用
// 提供网络请求优化、缓存管理、离线支持等功能
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;