miniprogramme/utils/network-optimizer.js
2025-09-12 16:08:17 +08:00

684 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 网络优化管理器 - 微信小程序专用
// 提供网络请求优化、缓存管理、离线支持等功能
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;
console.log('🌐 初始化网络优化器...');
try {
// 获取网络状态
await this.updateNetworkState();
// 加载缓存数据
await this.loadCacheFromStorage();
// 设置网络监听
this.setupNetworkListeners();
// 启动请求队列处理
this.startRequestQueueProcessor();
// 启动缓存清理
this.startCacheCleanup();
// 启动预加载处理
this.startPreloadProcessor();
this.isInitialized = true;
console.log('✅ 网络优化器初始化完成');
} 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) => {
console.log('🌐 网络状态变化:', 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++;
console.log('📦 缓存命中:', key);
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
});
console.log('📦 缓存设置:', key);
// 异步保存到本地存储
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');
console.log('🧹 缓存已清空');
}
// 获取缓存大小
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) {
console.log('🧹 清理缓存:', keysToDelete.length, '项');
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
});
console.log('📱 添加到离线队列:', request.url);
this.stats.offlineRequests++;
}
// 处理离线队列
async processOfflineQueue() {
if (!this.networkState.isOnline || this.offlineQueue.length === 0) return;
console.log('📱 处理离线队列:', this.offlineQueue.length, '个请求');
const queue = [...this.offlineQueue];
this.offlineQueue = [];
for (const request of queue) {
try {
await this.makeRequest(request);
console.log('✅ 离线请求同步成功:', request.url);
} 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()
});
console.log('🚀 添加预加载请求:', url);
}
// 启动预加载处理器
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
});
console.log('🚀 预加载完成:', request.url);
} 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));
}
console.log('📦 从本地存储加载缓存:', this.cache.size, '项');
} 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;
console.log('🌐 网络优化器已销毁');
}
}
// 创建全局实例
const networkOptimizer = new NetworkOptimizer();
module.exports = networkOptimizer;