Initial Commit
This commit is contained in:
commit
1d71a02738
237 changed files with 64293 additions and 0 deletions
684
utils/network-optimizer.js
Normal file
684
utils/network-optimizer.js
Normal file
|
|
@ -0,0 +1,684 @@
|
|||
// 网络优化管理器 - 微信小程序专用
|
||||
// 提供网络请求优化、缓存管理、离线支持等功能
|
||||
|
||||
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;
|
||||
Loading…
Add table
Add a link
Reference in a new issue