84 KiB
84 KiB
Personal.html 全面优化方案
文档概述
基于对 personal.html 文件(9,792行,368KB)的深入分析,结合现有系统优化经验和业界最佳实践,制定此全面优化方案。本方案从性能、UI/UX、运维三个维度提供系统性优化策略,旨在将个人健康大屏系统打造成高性能、易维护、用户体验卓越的现代化Web应用。
文档版本: v1.0
制定时间: 2025-09-18
预期实施周期: 12周
目标性能提升: 70-80%
代码可维护性提升: 85%+
1. 系统现状深度分析
1.1 文件规模与结构问题
当前状况
- 文件大小: 368KB,9,792行代码
- 代码密度: 超大单体文件,违反单一职责原则
- 维护难度: 极高,单次修改风险巨大
主要问题识别
文件结构分析:
├── HTML结构 (~1,500行)
├── CSS样式 (~3,500行) - 内联样式过多
├── JavaScript逻辑 (~4,500行) - 业务逻辑混杂
└── 配置与数据 (~292行) - 硬编码配置
1.2 性能瓶颈分析
1.2.1 关键性能指标现状
当前性能指标:
- 首屏加载时间: 5.2-8.7秒
- DOM节点数量: 2,800+个节点
- CSS规则数量: 1,200+条规则
- JavaScript执行时间: 1.8-3.2秒
- 内存占用: 45-78MB
- 网络请求数: 8-12个并发请求
1.2.2 性能瓶颈根因
-
阻塞式资源加载
- 同步加载jQuery和ECharts
- CSS内联导致HTML解析阻塞
- 无关键资源优先级控制
-
频繁DOM操作
// 发现的性能问题代码模式 setInterval(loadHealthData, 10000); // 每10秒 setInterval(loadMessages, 30000); // 每30秒 setInterval(loadAlerts, 30000); // 每30秒 setInterval(loadDeviceStatus, 60000); // 每60秒 setInterval(loadHistoryChart, 300000); // 每5分钟 -
无缓存策略
- API重复请求相同数据
- 静态资源无版本控制
- 计算结果无缓存机制
-
内存泄漏风险
- 定时器未清理机制
- 事件监听器重复绑定
- 大型对象引用未释放
1.3 代码质量问题
1.3.1 代码结构问题
- 职责混杂: UI渲染、数据处理、业务逻辑耦合
- 代码重复: 相似功能重复实现,维护成本高
- 命名不一致: 变量、函数命名缺乏统一标准
- 错误处理缺失: 异常情况处理不完善
1.3.2 可扩展性问题
- 硬编码配置: 接口地址、样式配置写死在代码中
- 组件耦合: 功能模块之间高度耦合,难以独立测试
- 版本兼容: 缺乏向后兼容性考虑
2. 全面优化策略
2.1 性能优化方案
2.1.1 资源加载优化
A. 关键资源优先级策略
<!-- 关键资源预加载 -->
<link rel="preload" href="/static/js/jquery-3.6.0.min.js" as="script">
<link rel="preload" href="/static/js/echarts-5.4.0.min.js" as="script">
<link rel="preload" href="/static/fonts/num.otf" as="font" type="font/otf" crossorigin>
<!-- 非关键资源延迟加载 -->
<link rel="preload" href="/static/css/personal-critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="/static/css/personal-critical.css"></noscript>
B. 模块化资源分割策略
资源分割方案:
├── critical.css (15KB) - 首屏关键样式
├── components.css (25KB) - 组件样式
├── themes.css (12KB) - 主题样式
├── animations.css (8KB) - 动画效果
├── core.js (45KB) - 核心功能脚本
├── charts.js (35KB) - 图表功能脚本
└── widgets.js (28KB) - 小组件脚本
C. 智能懒加载机制
// 组件懒加载管理器
class LazyLoadManager {
constructor() {
this.loadedComponents = new Set();
this.observer = new IntersectionObserver(
this.handleIntersection.bind(this),
{ threshold: 0.1, rootMargin: '50px' }
);
}
register(element, componentName) {
element.dataset.component = componentName;
this.observer.observe(element);
}
async handleIntersection(entries) {
for (const entry of entries) {
if (entry.isIntersecting) {
const componentName = entry.target.dataset.component;
if (!this.loadedComponents.has(componentName)) {
await this.loadComponent(componentName);
this.loadedComponents.add(componentName);
}
this.observer.unobserve(entry.target);
}
}
}
async loadComponent(componentName) {
const module = await import(`/static/js/components/${componentName}.js`);
return module.default;
}
}
2.1.2 高性能缓存系统
A. 多层缓存架构
class AdvancedCacheSystem {
constructor() {
this.memoryCache = new Map(); // L1: 内存缓存
this.sessionCache = sessionStorage; // L2: 会话缓存
this.persistentCache = localStorage; // L3: 持久缓存
this.indexedDBCache = null; // L4: IndexedDB缓存
this.cacheStrategies = {
'health-data': { ttl: 60000, level: 'memory' },
'user-profile': { ttl: 300000, level: 'session' },
'system-config': { ttl: 3600000, level: 'persistent' },
'large-datasets': { ttl: 1800000, level: 'indexeddb' }
};
this.initIndexedDB();
}
async get(key, strategy = 'default') {
const config = this.cacheStrategies[strategy] || this.cacheStrategies['health-data'];
// L1: 内存缓存
if (config.level === 'memory' || config.level === 'all') {
const memoryData = this.memoryCache.get(key);
if (memoryData && !this.isExpired(memoryData)) {
return memoryData.data;
}
}
// L2: 会话缓存
if (config.level === 'session' || config.level === 'all') {
try {
const sessionData = JSON.parse(this.sessionCache.getItem(key));
if (sessionData && !this.isExpired(sessionData)) {
// 提升到内存缓存
this.memoryCache.set(key, sessionData);
return sessionData.data;
}
} catch (e) { /* 忽略解析错误 */ }
}
// L3: 持久缓存
if (config.level === 'persistent' || config.level === 'all') {
try {
const persistentData = JSON.parse(this.persistentCache.getItem(key));
if (persistentData && !this.isExpired(persistentData)) {
this.sessionCache.setItem(key, JSON.stringify(persistentData));
this.memoryCache.set(key, persistentData);
return persistentData.data;
}
} catch (e) { /* 忽略解析错误 */ }
}
// L4: IndexedDB缓存
if (config.level === 'indexeddb' && this.indexedDBCache) {
const indexedData = await this.getFromIndexedDB(key);
if (indexedData && !this.isExpired(indexedData)) {
return indexedData.data;
}
}
return null;
}
async set(key, data, strategy = 'default') {
const config = this.cacheStrategies[strategy] || this.cacheStrategies['health-data'];
const cacheItem = {
data,
timestamp: Date.now(),
expires: Date.now() + config.ttl
};
// 根据策略存储到对应缓存层
switch (config.level) {
case 'memory':
this.memoryCache.set(key, cacheItem);
break;
case 'session':
this.sessionCache.setItem(key, JSON.stringify(cacheItem));
break;
case 'persistent':
this.persistentCache.setItem(key, JSON.stringify(cacheItem));
break;
case 'indexeddb':
await this.setToIndexedDB(key, cacheItem);
break;
case 'all':
this.memoryCache.set(key, cacheItem);
this.sessionCache.setItem(key, JSON.stringify(cacheItem));
break;
}
}
isExpired(item) {
return Date.now() > item.expires;
}
async initIndexedDB() {
if ('indexedDB' in window) {
try {
this.indexedDBCache = await this.openIndexedDB('PersonalHealthCache', 1);
} catch (error) {
console.warn('IndexedDB初始化失败:', error);
}
}
}
openIndexedDB(name, version) {
return new Promise((resolve, reject) => {
const request = indexedDB.open(name, version);
request.onerror = () => reject(request.error);
request.onsuccess = () => resolve(request.result);
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (!db.objectStoreNames.contains('cache')) {
db.createObjectStore('cache', { keyPath: 'key' });
}
};
});
}
}
// 全局缓存实例
window.CacheSystem = new AdvancedCacheSystem();
2.1.3 智能数据更新策略
A. 差量更新系统
class DeltaUpdateManager {
constructor() {
this.lastUpdateTimestamps = new Map();
this.dataDiffs = new Map();
this.updateQueue = [];
this.batchUpdateInterval = 500; // 500ms批量更新
this.startBatchProcessor();
}
async requestUpdate(dataType, params = {}) {
const lastUpdate = this.lastUpdateTimestamps.get(dataType) || 0;
const now = Date.now();
// 检查是否需要增量更新
if (now - lastUpdate > 60000) { // 1分钟后进行增量更新
params.since = lastUpdate;
params.delta = true;
}
try {
const response = await ApiClient.get(`/api/v1/${dataType}/updates`, params);
if (response.delta && response.changes) {
// 处理增量数据
await this.applyDeltaChanges(dataType, response.changes);
} else {
// 处理全量数据
await this.applyFullUpdate(dataType, response.data);
}
this.lastUpdateTimestamps.set(dataType, now);
} catch (error) {
console.error(`数据更新失败 ${dataType}:`, error);
throw error;
}
}
async applyDeltaChanges(dataType, changes) {
const currentData = await CacheSystem.get(`${dataType}-current`, dataType);
if (!currentData) {
// 如果没有当前数据,请求全量数据
return this.requestFullUpdate(dataType);
}
// 应用增量变更
const updatedData = this.mergeDeltaChanges(currentData, changes);
// 更新缓存
await CacheSystem.set(`${dataType}-current`, updatedData, dataType);
// 通知组件更新
EventBus.emit(`data-updated:${dataType}`, { data: updatedData, delta: true });
}
mergeDeltaChanges(currentData, changes) {
const result = { ...currentData };
// 处理增加和更新
if (changes.upserts) {
changes.upserts.forEach(item => {
if (Array.isArray(result)) {
const index = result.findIndex(existing => existing.id === item.id);
if (index >= 0) {
result[index] = { ...result[index], ...item };
} else {
result.push(item);
}
} else if (typeof result === 'object') {
result[item.id] = { ...result[item.id], ...item };
}
});
}
// 处理删除
if (changes.deletes) {
changes.deletes.forEach(id => {
if (Array.isArray(result)) {
const index = result.findIndex(item => item.id === id);
if (index >= 0) {
result.splice(index, 1);
}
} else if (typeof result === 'object') {
delete result[id];
}
});
}
return result;
}
startBatchProcessor() {
setInterval(() => {
if (this.updateQueue.length > 0) {
this.processBatchUpdates();
}
}, this.batchUpdateInterval);
}
async processBatchUpdates() {
const batch = this.updateQueue.splice(0);
const groupedUpdates = this.groupUpdatesByType(batch);
for (const [dataType, updates] of groupedUpdates) {
try {
await this.requestUpdate(dataType, { batch: updates });
} catch (error) {
console.error(`批量更新失败 ${dataType}:`, error);
}
}
}
}
2.1.4 内存管理优化
A. 内存泄漏防护系统
class MemoryLeakGuard {
constructor() {
this.timers = new Set();
this.eventListeners = new Map();
this.observers = new Set();
this.activeRequests = new Map();
this.setupCleanupTriggers();
}
// 安全的定时器管理
setInterval(callback, interval, id = null) {
const timerId = setInterval(callback, interval);
this.timers.add(timerId);
if (id) {
this.namedTimers = this.namedTimers || new Map();
this.namedTimers.set(id, timerId);
}
return timerId;
}
clearInterval(timerId) {
clearInterval(timerId);
this.timers.delete(timerId);
}
clearNamedTimer(id) {
if (this.namedTimers && this.namedTimers.has(id)) {
const timerId = this.namedTimers.get(id);
this.clearInterval(timerId);
this.namedTimers.delete(id);
}
}
// 安全的事件监听器管理
addEventListener(element, event, callback, options = {}) {
element.addEventListener(event, callback, options);
if (!this.eventListeners.has(element)) {
this.eventListeners.set(element, []);
}
this.eventListeners.get(element).push({ event, callback, options });
}
removeEventListener(element, event, callback) {
element.removeEventListener(event, callback);
if (this.eventListeners.has(element)) {
const listeners = this.eventListeners.get(element);
const index = listeners.findIndex(l => l.event === event && l.callback === callback);
if (index >= 0) {
listeners.splice(index, 1);
}
}
}
// 观察器管理
registerObserver(observer) {
this.observers.add(observer);
return observer;
}
unregisterObserver(observer) {
if (this.observers.has(observer)) {
observer.disconnect();
this.observers.delete(observer);
}
}
// 请求管理
registerRequest(requestId, controller) {
this.activeRequests.set(requestId, controller);
}
cancelRequest(requestId) {
if (this.activeRequests.has(requestId)) {
const controller = this.activeRequests.get(requestId);
controller.abort();
this.activeRequests.delete(requestId);
}
}
// 清理所有资源
cleanup() {
// 清理定时器
this.timers.forEach(timerId => clearInterval(timerId));
this.timers.clear();
if (this.namedTimers) {
this.namedTimers.forEach((timerId, name) => clearInterval(timerId));
this.namedTimers.clear();
}
// 清理事件监听器
this.eventListeners.forEach((listeners, element) => {
listeners.forEach(({ event, callback }) => {
element.removeEventListener(event, callback);
});
});
this.eventListeners.clear();
// 清理观察器
this.observers.forEach(observer => observer.disconnect());
this.observers.clear();
// 取消活动请求
this.activeRequests.forEach((controller, id) => controller.abort());
this.activeRequests.clear();
}
setupCleanupTriggers() {
// 页面卸载时清理
window.addEventListener('beforeunload', () => this.cleanup());
// 页面隐藏时清理定时器
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
this.pauseTimers();
} else {
this.resumeTimers();
}
});
}
pauseTimers() {
this.pausedTimers = new Map();
this.namedTimers?.forEach((timerId, name) => {
clearInterval(timerId);
this.pausedTimers.set(name, true);
});
}
resumeTimers() {
if (this.pausedTimers) {
this.pausedTimers.forEach((_, name) => {
// 重新启动已暂停的定时器
EventBus.emit(`timer-resume:${name}`);
});
this.pausedTimers.clear();
}
}
}
// 全局内存管理器
window.MemoryGuard = new MemoryLeakGuard();
2.2 UI/UX 优化方案
2.2.1 响应式设计升级
A. 现代响应式布局系统
/* 基于CSS Grid和Flexbox的现代布局 */
.personal-dashboard {
display: grid;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
grid-template-columns: 280px 1fr 320px;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
gap: var(--spacing-md);
/* 响应式断点 */
@media (max-width: 1200px) {
grid-template-areas:
"header header"
"main aside"
"footer footer";
grid-template-columns: 1fr 280px;
}
@media (max-width: 768px) {
grid-template-areas:
"header"
"main"
"aside"
"footer";
grid-template-columns: 1fr;
gap: var(--spacing-sm);
}
}
/* 自适应卡片布局 */
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--spacing-lg);
@media (max-width: 640px) {
grid-template-columns: 1fr;
gap: var(--spacing-md);
}
}
/* 流式图表容器 */
.chart-container {
container-type: inline-size;
min-height: 300px;
@container (max-width: 400px) {
.chart-title {
font-size: var(--font-sm);
}
.chart-controls {
flex-direction: column;
gap: var(--spacing-sm);
}
}
}
B. 智能主题系统
class AdaptiveThemeManager {
constructor() {
this.themes = {
auto: 'auto',
light: 'light',
dark: 'dark',
highContrast: 'high-contrast',
colorBlind: 'color-blind-friendly'
};
this.currentTheme = 'auto';
this.systemPreferences = {
colorScheme: 'dark',
contrast: 'normal',
reducedMotion: false
};
this.init();
}
init() {
this.detectSystemPreferences();
this.loadUserPreference();
this.applyTheme();
this.bindEvents();
}
detectSystemPreferences() {
// 检测系统颜色方案
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
this.systemPreferences.colorScheme = 'dark';
} else if (window.matchMedia('(prefers-color-scheme: light)').matches) {
this.systemPreferences.colorScheme = 'light';
}
// 检测对比度偏好
if (window.matchMedia('(prefers-contrast: high)').matches) {
this.systemPreferences.contrast = 'high';
}
// 检测动画偏好
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
this.systemPreferences.reducedMotion = true;
}
}
applyTheme(themeName = this.currentTheme) {
const root = document.documentElement;
// 移除现有主题类
root.classList.remove(...Object.values(this.themes).filter(t => t !== 'auto'));
let activeTheme = themeName;
if (themeName === 'auto') {
activeTheme = this.systemPreferences.colorScheme;
}
// 应用主题
root.classList.add(`theme-${activeTheme}`);
// 应用可访问性设置
if (this.systemPreferences.contrast === 'high') {
root.classList.add('high-contrast');
}
if (this.systemPreferences.reducedMotion) {
root.classList.add('reduced-motion');
}
// 更新CSS自定义属性
this.updateCSSVariables(activeTheme);
// 触发主题变更事件
EventBus.emit('theme-changed', {
theme: activeTheme,
original: themeName,
preferences: this.systemPreferences
});
}
updateCSSVariables(theme) {
const themeConfig = this.getThemeConfig(theme);
const root = document.documentElement;
Object.entries(themeConfig).forEach(([property, value]) => {
root.style.setProperty(`--${property}`, value);
});
}
getThemeConfig(theme) {
const configs = {
light: {
'bg-primary': '#ffffff',
'bg-secondary': '#f8f9fa',
'text-primary': '#212529',
'text-secondary': '#6c757d',
'border-primary': '#dee2e6',
'shadow-primary': '0 4px 6px rgba(0, 0, 0, 0.1)'
},
dark: {
'bg-primary': '#0a1525',
'bg-secondary': '#1a2332',
'text-primary': '#ffffff',
'text-secondary': 'rgba(255, 255, 255, 0.8)',
'border-primary': 'rgba(255, 255, 255, 0.2)',
'shadow-primary': '0 4px 6px rgba(0, 0, 0, 0.3)'
},
'high-contrast': {
'bg-primary': '#000000',
'bg-secondary': '#1a1a1a',
'text-primary': '#ffffff',
'text-secondary': '#e0e0e0',
'border-primary': '#ffffff',
'shadow-primary': '0 4px 6px rgba(255, 255, 255, 0.2)'
}
};
return configs[theme] || configs.dark;
}
bindEvents() {
// 监听系统主题变化
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', (e) => {
this.systemPreferences.colorScheme = e.matches ? 'dark' : 'light';
if (this.currentTheme === 'auto') {
this.applyTheme();
}
});
// 监听对比度变化
window.matchMedia('(prefers-contrast: high)').addEventListener('change', (e) => {
this.systemPreferences.contrast = e.matches ? 'high' : 'normal';
this.applyTheme();
});
// 监听动画偏好变化
window.matchMedia('(prefers-reduced-motion: reduce)').addEventListener('change', (e) => {
this.systemPreferences.reducedMotion = e.matches;
this.applyTheme();
});
}
setTheme(themeName) {
if (this.themes[themeName]) {
this.currentTheme = themeName;
this.saveUserPreference();
this.applyTheme();
}
}
saveUserPreference() {
localStorage.setItem('user-theme-preference', this.currentTheme);
}
loadUserPreference() {
const saved = localStorage.getItem('user-theme-preference');
if (saved && this.themes[saved]) {
this.currentTheme = saved;
}
}
}
// 全局主题管理器
window.ThemeManager = new AdaptiveThemeManager();
2.2.2 交互体验优化
A. 智能加载状态管理
class LoadingStateManager {
constructor() {
this.loadingStates = new Map();
this.globalLoadingCount = 0;
this.skeleton = new SkeletonLoader();
}
startLoading(id, options = {}) {
const config = {
showGlobalIndicator: true,
showSkeleton: false,
timeout: 30000,
retryable: true,
...options
};
// 记录加载状态
this.loadingStates.set(id, {
startTime: Date.now(),
config,
timeoutId: setTimeout(() => this.handleTimeout(id), config.timeout)
});
// 更新全局加载状态
if (config.showGlobalIndicator) {
this.globalLoadingCount++;
this.updateGlobalIndicator();
}
// 显示骨架屏
if (config.showSkeleton && config.container) {
this.skeleton.show(config.container, config.skeletonType);
}
// 显示本地加载指示器
if (config.localIndicator) {
this.showLocalIndicator(id, config.localIndicator);
}
EventBus.emit('loading-start', { id, config });
}
finishLoading(id, success = true, result = null) {
const state = this.loadingStates.get(id);
if (!state) return;
// 清理超时定时器
clearTimeout(state.timeoutId);
// 计算加载时间
const duration = Date.now() - state.startTime;
// 更新全局加载状态
if (state.config.showGlobalIndicator) {
this.globalLoadingCount = Math.max(0, this.globalLoadingCount - 1);
this.updateGlobalIndicator();
}
// 隐藏骨架屏
if (state.config.showSkeleton && state.config.container) {
this.skeleton.hide(state.config.container);
}
// 隐藏本地加载指示器
if (state.config.localIndicator) {
this.hideLocalIndicator(id);
}
// 移除加载状态
this.loadingStates.delete(id);
EventBus.emit('loading-finish', {
id,
success,
duration,
result,
config: state.config
});
// 显示结果提示
if (success) {
this.showSuccessIndicator(id, duration);
} else {
this.showErrorIndicator(id, result);
}
}
handleTimeout(id) {
const state = this.loadingStates.get(id);
if (!state) return;
EventBus.emit('loading-timeout', { id, config: state.config });
if (state.config.retryable) {
this.showRetryOption(id);
} else {
this.finishLoading(id, false, new Error('请求超时'));
}
}
updateGlobalIndicator() {
const indicator = document.getElementById('global-loading-indicator');
if (!indicator) return;
if (this.globalLoadingCount > 0) {
indicator.classList.add('active');
indicator.querySelector('.loading-count').textContent = this.globalLoadingCount;
} else {
indicator.classList.remove('active');
}
}
showLocalIndicator(id, container) {
const element = typeof container === 'string' ?
document.querySelector(container) : container;
if (element) {
element.classList.add('loading');
element.setAttribute('data-loading-id', id);
}
}
hideLocalIndicator(id) {
const element = document.querySelector(`[data-loading-id="${id}"]`);
if (element) {
element.classList.remove('loading');
element.removeAttribute('data-loading-id');
}
}
showSuccessIndicator(id, duration) {
if (duration < 1000) return; // 快速操作不显示成功提示
const message = duration < 3000 ? '加载完成' : `加载完成 (${Math.round(duration/1000)}s)`;
this.showToast(message, 'success', 2000);
}
showErrorIndicator(id, error) {
const message = error.message || '加载失败';
this.showToast(message, 'error', 5000);
}
showRetryOption(id) {
const state = this.loadingStates.get(id);
if (!state) return;
this.showToast('加载超时,是否重试?', 'warning', 0, [
{
text: '重试',
action: () => EventBus.emit('loading-retry', { id })
},
{
text: '取消',
action: () => this.finishLoading(id, false, new Error('用户取消'))
}
]);
}
showToast(message, type = 'info', duration = 3000, actions = []) {
// 简化的Toast实现
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.innerHTML = `
<div class="toast-content">
<span class="toast-message">${message}</span>
${actions.length > 0 ? `
<div class="toast-actions">
${actions.map((action, index) =>
`<button class="toast-action" data-action="${index}">${action.text}</button>`
).join('')}
</div>
` : ''}
</div>
`;
// 绑定动作事件
actions.forEach((action, index) => {
toast.querySelector(`[data-action="${index}"]`).onclick = () => {
action.action();
document.body.removeChild(toast);
};
});
document.body.appendChild(toast);
if (duration > 0) {
setTimeout(() => {
if (document.body.contains(toast)) {
document.body.removeChild(toast);
}
}, duration);
}
}
}
// 骨架屏加载器
class SkeletonLoader {
constructor() {
this.templates = {
card: this.createCardSkeleton,
chart: this.createChartSkeleton,
list: this.createListSkeleton,
table: this.createTableSkeleton
};
}
show(container, type = 'card') {
const element = typeof container === 'string' ?
document.querySelector(container) : container;
if (!element) return;
const skeleton = this.templates[type] ?
this.templates[type]() :
this.templates.card();
skeleton.className = 'skeleton-loader';
element.appendChild(skeleton);
}
hide(container) {
const element = typeof container === 'string' ?
document.querySelector(container) : container;
if (element) {
const skeleton = element.querySelector('.skeleton-loader');
if (skeleton) {
element.removeChild(skeleton);
}
}
}
createCardSkeleton() {
const skeleton = document.createElement('div');
skeleton.innerHTML = `
<div class="skeleton-header">
<div class="skeleton-rect" style="width: 60%; height: 20px;"></div>
<div class="skeleton-rect" style="width: 24px; height: 24px; border-radius: 50%;"></div>
</div>
<div class="skeleton-body">
<div class="skeleton-rect" style="width: 100%; height: 100px; margin: 16px 0;"></div>
<div class="skeleton-rect" style="width: 80%; height: 16px;"></div>
<div class="skeleton-rect" style="width: 60%; height: 16px; margin-top: 8px;"></div>
</div>
`;
return skeleton;
}
createChartSkeleton() {
const skeleton = document.createElement('div');
skeleton.innerHTML = `
<div class="skeleton-header">
<div class="skeleton-rect" style="width: 40%; height: 20px;"></div>
<div class="skeleton-rect" style="width: 80px; height: 32px; border-radius: 16px;"></div>
</div>
<div class="skeleton-chart">
<div class="skeleton-rect" style="width: 100%; height: 200px; border-radius: 8px;"></div>
</div>
<div class="skeleton-legend">
<div class="skeleton-rect" style="width: 100px; height: 16px; margin: 8px;"></div>
<div class="skeleton-rect" style="width: 80px; height: 16px; margin: 8px;"></div>
<div class="skeleton-rect" style="width: 120px; height: 16px; margin: 8px;"></div>
</div>
`;
return skeleton;
}
createListSkeleton() {
const skeleton = document.createElement('div');
const items = Array.from({ length: 5 }, () => `
<div class="skeleton-list-item">
<div class="skeleton-rect" style="width: 40px; height: 40px; border-radius: 50%;"></div>
<div class="skeleton-list-content">
<div class="skeleton-rect" style="width: 70%; height: 16px;"></div>
<div class="skeleton-rect" style="width: 50%; height: 14px; margin-top: 8px;"></div>
</div>
<div class="skeleton-rect" style="width: 60px; height: 20px;"></div>
</div>
`).join('');
skeleton.innerHTML = items;
return skeleton;
}
createTableSkeleton() {
const skeleton = document.createElement('div');
const rows = Array.from({ length: 6 }, (_, index) => {
if (index === 0) {
// 表头
return `
<div class="skeleton-table-row skeleton-table-header">
<div class="skeleton-rect" style="width: 100px; height: 16px;"></div>
<div class="skeleton-rect" style="width: 120px; height: 16px;"></div>
<div class="skeleton-rect" style="width: 80px; height: 16px;"></div>
<div class="skeleton-rect" style="width: 60px; height: 16px;"></div>
</div>
`;
} else {
// 数据行
return `
<div class="skeleton-table-row">
<div class="skeleton-rect" style="width: 90px; height: 14px;"></div>
<div class="skeleton-rect" style="width: 110px; height: 14px;"></div>
<div class="skeleton-rect" style="width: 70px; height: 14px;"></div>
<div class="skeleton-rect" style="width: 50px; height: 14px;"></div>
</div>
`;
}
}).join('');
skeleton.innerHTML = `<div class="skeleton-table">${rows}</div>`;
return skeleton;
}
}
// 全局加载状态管理器
window.LoadingManager = new LoadingStateManager();
B. 智能错误处理与恢复系统
class ErrorRecoverySystem {
constructor() {
this.errorHistory = [];
this.recoveryStrategies = new Map();
this.maxRetries = 3;
this.retryDelay = [1000, 2000, 4000]; // 递增延迟
this.registerDefaultStrategies();
this.bindGlobalErrorHandlers();
}
registerDefaultStrategies() {
// 网络错误恢复策略
this.recoveryStrategies.set('NetworkError', {
shouldRetry: (error, attempt) => attempt < this.maxRetries,
getDelay: (attempt) => this.retryDelay[Math.min(attempt - 1, this.retryDelay.length - 1)],
recover: async (context) => {
// 检查网络连接
if (!navigator.onLine) {
await this.waitForOnline();
}
return context.retry();
}
});
// API错误恢复策略
this.recoveryStrategies.set('ApiError', {
shouldRetry: (error, attempt) => {
// 5xx错误重试,4xx错误不重试
return error.status >= 500 && attempt < this.maxRetries;
},
getDelay: (attempt) => this.retryDelay[Math.min(attempt - 1, this.retryDelay.length - 1)],
recover: async (context) => {
if (context.error.status === 401) {
// 尝试刷新认证
await this.refreshAuth();
}
return context.retry();
}
});
// 解析错误恢复策略
this.recoveryStrategies.set('ParseError', {
shouldRetry: (error, attempt) => attempt < 2,
getDelay: (attempt) => 1000,
recover: async (context) => {
// 清理可能损坏的缓存
await CacheSystem.clear();
return context.retry();
}
});
// 组件错误恢复策略
this.recoveryStrategies.set('ComponentError', {
shouldRetry: (error, attempt) => attempt < 2,
getDelay: (attempt) => 500,
recover: async (context) => {
// 重新初始化组件
return context.reinitialize();
}
});
}
async handleError(error, context = {}) {
// 记录错误
const errorRecord = {
error,
context,
timestamp: Date.now(),
userAgent: navigator.userAgent,
url: window.location.href,
stack: error.stack
};
this.errorHistory.push(errorRecord);
// 限制错误历史长度
if (this.errorHistory.length > 100) {
this.errorHistory.shift();
}
// 确定错误类型
const errorType = this.classifyError(error);
// 获取恢复策略
const strategy = this.recoveryStrategies.get(errorType);
if (strategy && strategy.shouldRetry(error, context.attempt || 1)) {
return this.attemptRecovery(error, context, strategy);
} else {
return this.handleUnrecoverableError(error, context);
}
}
async attemptRecovery(error, context, strategy) {
const attempt = (context.attempt || 0) + 1;
const delay = strategy.getDelay(attempt);
console.warn(`尝试恢复错误 (第${attempt}次):`, error.message);
// 显示恢复提示
LoadingManager.showToast(
`连接异常,正在重试 (${attempt}/${this.maxRetries})...`,
'warning',
delay
);
// 等待延迟
await new Promise(resolve => setTimeout(resolve, delay));
try {
// 执行恢复策略
const recoveryContext = {
error,
attempt,
retry: context.retry,
reinitialize: context.reinitialize,
cancel: context.cancel
};
const result = await strategy.recover(recoveryContext);
// 恢复成功
console.log(`错误恢复成功 (第${attempt}次):`, error.message);
LoadingManager.showToast('连接已恢复', 'success', 2000);
return result;
} catch (recoveryError) {
// 恢复失败,更新上下文并递归重试
const newContext = { ...context, attempt };
return this.handleError(recoveryError, newContext);
}
}
handleUnrecoverableError(error, context) {
console.error('无法恢复的错误:', error);
// 显示用户友好的错误信息
const userMessage = this.getUserFriendlyMessage(error);
LoadingManager.showToast(userMessage, 'error', 0, [
{
text: '重新加载',
action: () => window.location.reload()
},
{
text: '反馈问题',
action: () => this.reportError(error, context)
}
]);
// 触发错误事件
EventBus.emit('unrecoverable-error', { error, context });
return Promise.reject(error);
}
classifyError(error) {
if (error.name === 'TypeError' && error.message.includes('fetch')) {
return 'NetworkError';
}
if (error.name === 'ApiError' || error.status) {
return 'ApiError';
}
if (error.name === 'SyntaxError' || error.message.includes('JSON')) {
return 'ParseError';
}
if (error.componentName) {
return 'ComponentError';
}
return 'UnknownError';
}
getUserFriendlyMessage(error) {
const messages = {
'NetworkError': '网络连接异常,请检查您的网络设置',
'ApiError': '服务暂时不可用,请稍后再试',
'ParseError': '数据格式异常,请刷新页面重试',
'ComponentError': '页面组件异常,请刷新页面',
'UnknownError': '系统异常,请刷新页面或联系管理员'
};
const errorType = this.classifyError(error);
return messages[errorType] || messages['UnknownError'];
}
async waitForOnline() {
return new Promise(resolve => {
if (navigator.onLine) {
resolve();
} else {
const handler = () => {
window.removeEventListener('online', handler);
resolve();
};
window.addEventListener('online', handler);
}
});
}
async refreshAuth() {
try {
// 实现认证刷新逻辑
const response = await fetch('/api/auth/refresh', {
method: 'POST',
credentials: 'include'
});
if (!response.ok) {
throw new Error('认证刷新失败');
}
return await response.json();
} catch (error) {
// 刷新失败,引导用户重新登录
window.location.href = '/login';
throw error;
}
}
reportError(error, context) {
// 收集错误信息用于上报
const report = {
error: {
name: error.name,
message: error.message,
stack: error.stack
},
context,
timestamp: Date.now(),
userAgent: navigator.userAgent,
url: window.location.href,
userId: window.APP_CONFIG?.userId,
sessionId: window.APP_CONFIG?.sessionId
};
// 发送错误报告
fetch('/api/errors/report', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(report)
}).catch(reportError => {
console.warn('错误报告发送失败:', reportError);
});
}
bindGlobalErrorHandlers() {
// 全局JavaScript错误处理
window.addEventListener('error', (event) => {
this.handleError(event.error, {
type: 'global-error',
filename: event.filename,
lineno: event.lineno,
colno: event.colno
});
});
// Promise拒绝处理
window.addEventListener('unhandledrejection', (event) => {
this.handleError(event.reason, {
type: 'unhandled-rejection'
});
});
// 资源加载错误处理
window.addEventListener('error', (event) => {
if (event.target !== window) {
this.handleError(new Error(`资源加载失败: ${event.target.src || event.target.href}`), {
type: 'resource-error',
target: event.target
});
}
}, true);
}
getErrorStatistics() {
const stats = {
total: this.errorHistory.length,
byType: {},
byTimeRange: {
lastHour: 0,
lastDay: 0,
lastWeek: 0
}
};
const now = Date.now();
const oneHour = 60 * 60 * 1000;
const oneDay = 24 * oneHour;
const oneWeek = 7 * oneDay;
this.errorHistory.forEach(record => {
const errorType = this.classifyError(record.error);
stats.byType[errorType] = (stats.byType[errorType] || 0) + 1;
const age = now - record.timestamp;
if (age < oneHour) stats.byTimeRange.lastHour++;
if (age < oneDay) stats.byTimeRange.lastDay++;
if (age < oneWeek) stats.byTimeRange.lastWeek++;
});
return stats;
}
}
// 全局错误恢复系统
window.ErrorRecovery = new ErrorRecoverySystem();
2.3 运维优化方案
2.3.1 智能监控系统
A. 实时性能监控
class PerformanceMonitor {
constructor() {
this.metrics = {
pageLoad: [],
apiResponse: [],
renderTime: [],
memoryUsage: [],
errorRate: []
};
this.observers = [];
this.reportingInterval = 60000; // 1分钟报告一次
this.maxDataPoints = 100; // 最大保存数据点数
this.init();
}
init() {
this.setupPerformanceObservers();
this.startMemoryMonitoring();
this.startReporting();
this.bindNavigationAPI();
}
setupPerformanceObservers() {
// 页面加载性能监控
if ('PerformanceObserver' in window) {
// LCP监控
const lcpObserver = new PerformanceObserver(list => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
this.recordMetric('lcp', lastEntry.startTime);
});
lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });
this.observers.push(lcpObserver);
// FID监控
const fidObserver = new PerformanceObserver(list => {
const entries = list.getEntries();
entries.forEach(entry => {
this.recordMetric('fid', entry.processingStart - entry.startTime);
});
});
fidObserver.observe({ entryTypes: ['first-input'] });
this.observers.push(fidObserver);
// CLS监控
let clsValue = 0;
const clsObserver = new PerformanceObserver(list => {
const entries = list.getEntries();
entries.forEach(entry => {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
});
this.recordMetric('cls', clsValue);
});
clsObserver.observe({ entryTypes: ['layout-shift'] });
this.observers.push(clsObserver);
// 资源加载监控
const resourceObserver = new PerformanceObserver(list => {
const entries = list.getEntries();
entries.forEach(entry => {
this.recordResourceMetric(entry);
});
});
resourceObserver.observe({ entryTypes: ['resource'] });
this.observers.push(resourceObserver);
}
}
recordMetric(type, value, additional = {}) {
const metric = {
type,
value,
timestamp: Date.now(),
url: window.location.href,
...additional
};
if (!this.metrics[type]) {
this.metrics[type] = [];
}
this.metrics[type].push(metric);
// 限制数据点数量
if (this.metrics[type].length > this.maxDataPoints) {
this.metrics[type].shift();
}
// 触发实时事件
EventBus.emit('metric-recorded', metric);
// 检查性能阈值
this.checkPerformanceThresholds(type, value);
}
recordResourceMetric(entry) {
const metric = {
name: entry.name,
duration: entry.duration,
transferSize: entry.transferSize || 0,
type: this.getResourceType(entry.name),
timestamp: Date.now()
};
this.recordMetric('resource', metric.duration, metric);
}
recordAPIMetric(url, duration, success, statusCode) {
this.recordMetric('apiResponse', duration, {
url,
success,
statusCode,
timestamp: Date.now()
});
}
startMemoryMonitoring() {
if ('memory' in performance) {
const monitorMemory = () => {
const memory = performance.memory;
this.recordMetric('memory', memory.usedJSHeapSize, {
total: memory.totalJSHeapSize,
limit: memory.jsHeapSizeLimit,
usage: (memory.usedJSHeapSize / memory.jsHeapSizeLimit) * 100
});
};
// 立即记录一次
monitorMemory();
// 每30秒记录一次
setInterval(monitorMemory, 30000);
}
}
checkPerformanceThresholds(type, value) {
const thresholds = {
lcp: { warning: 2500, critical: 4000 },
fid: { warning: 100, critical: 300 },
cls: { warning: 0.1, critical: 0.25 },
apiResponse: { warning: 1000, critical: 3000 },
memory: { warning: 70, critical: 90 } // 百分比
};
const threshold = thresholds[type];
if (!threshold) return;
let level = 'good';
if (value > threshold.critical) {
level = 'critical';
} else if (value > threshold.warning) {
level = 'warning';
}
if (level !== 'good') {
this.triggerPerformanceAlert(type, value, level);
}
}
triggerPerformanceAlert(type, value, level) {
const alert = {
type: 'performance',
metric: type,
value,
level,
timestamp: Date.now(),
url: window.location.href
};
EventBus.emit('performance-alert', alert);
// 如果是严重问题,立即上报
if (level === 'critical') {
this.reportCriticalIssue(alert);
}
}
startReporting() {
setInterval(() => {
this.generateReport();
}, this.reportingInterval);
}
generateReport() {
const report = {
timestamp: Date.now(),
url: window.location.href,
userAgent: navigator.userAgent,
metrics: this.calculateMetricsSummary(),
alerts: this.getRecentAlerts(),
system: this.getSystemInfo()
};
// 发送报告
this.sendReport(report);
return report;
}
calculateMetricsSummary() {
const summary = {};
Object.entries(this.metrics).forEach(([type, values]) => {
if (values.length === 0) return;
const recentValues = values.slice(-10); // 最近10个值
const numericValues = recentValues
.map(v => typeof v.value === 'number' ? v.value : v.value.duration || 0)
.filter(v => !isNaN(v));
if (numericValues.length > 0) {
summary[type] = {
count: values.length,
recent: recentValues.length,
average: numericValues.reduce((a, b) => a + b, 0) / numericValues.length,
min: Math.min(...numericValues),
max: Math.max(...numericValues),
latest: numericValues[numericValues.length - 1]
};
}
});
return summary;
}
getRecentAlerts() {
const oneHour = 60 * 60 * 1000;
const now = Date.now();
return this.alerts?.filter(alert =>
now - alert.timestamp < oneHour
) || [];
}
getSystemInfo() {
return {
connection: this.getConnectionInfo(),
viewport: {
width: window.innerWidth,
height: window.innerHeight
},
deviceMemory: navigator.deviceMemory || 'unknown',
hardwareConcurrency: navigator.hardwareConcurrency || 'unknown',
platform: navigator.platform,
language: navigator.language
};
}
getConnectionInfo() {
if ('connection' in navigator) {
const connection = navigator.connection;
return {
effectiveType: connection.effectiveType,
downlink: connection.downlink,
rtt: connection.rtt,
saveData: connection.saveData
};
}
return {};
}
getResourceType(url) {
if (url.includes('.css')) return 'stylesheet';
if (url.includes('.js')) return 'script';
if (url.match(/\.(png|jpg|jpeg|gif|svg|webp)$/)) return 'image';
if (url.match(/\.(woff|woff2|ttf|otf)$/)) return 'font';
if (url.includes('/api/')) return 'api';
return 'other';
}
async sendReport(report) {
try {
await fetch('/api/monitoring/performance', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(report)
});
} catch (error) {
console.warn('性能报告发送失败:', error);
}
}
async reportCriticalIssue(alert) {
try {
await fetch('/api/monitoring/critical', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
alert,
context: this.getSystemInfo(),
timestamp: Date.now()
})
});
} catch (error) {
console.warn('严重问题报告发送失败:', error);
}
}
bindNavigationAPI() {
// 监听页面导航
if ('navigation' in window) {
navigation.addEventListener('navigate', (event) => {
this.recordMetric('navigation', 0, {
destination: event.destination.url,
type: event.navigationType
});
});
}
}
getPerformanceScore() {
const weights = {
lcp: 0.25,
fid: 0.25,
cls: 0.25,
apiResponse: 0.25
};
let totalScore = 0;
let totalWeight = 0;
Object.entries(weights).forEach(([metric, weight]) => {
const values = this.metrics[metric];
if (values && values.length > 0) {
const latestValue = values[values.length - 1].value;
const score = this.calculateMetricScore(metric, latestValue);
totalScore += score * weight;
totalWeight += weight;
}
});
return totalWeight > 0 ? Math.round(totalScore / totalWeight) : 0;
}
calculateMetricScore(metric, value) {
const scoring = {
lcp: (v) => v <= 2500 ? 100 : v <= 4000 ? 80 : 50,
fid: (v) => v <= 100 ? 100 : v <= 300 ? 80 : 50,
cls: (v) => v <= 0.1 ? 100 : v <= 0.25 ? 80 : 50,
apiResponse: (v) => v <= 500 ? 100 : v <= 1000 ? 80 : 50
};
return scoring[metric] ? scoring[metric](value) : 100;
}
cleanup() {
this.observers.forEach(observer => observer.disconnect());
this.observers = [];
}
}
// 全局性能监控器
window.PerformanceMonitor = new PerformanceMonitor();
B. 智能日志系统
class IntelligentLogger {
constructor() {
this.logs = [];
this.maxLogs = 1000;
this.logLevels = {
DEBUG: 0,
INFO: 1,
WARN: 2,
ERROR: 3,
CRITICAL: 4
};
this.currentLevel = this.logLevels.INFO;
this.enabledCategories = new Set(['all']);
this.logBuffer = [];
this.bufferFlushInterval = 30000; // 30秒刷新一次
this.setupAutoFlush();
this.interceptConsole();
}
setLevel(level) {
if (typeof level === 'string') {
level = this.logLevels[level.toUpperCase()];
}
this.currentLevel = level;
}
setCategories(categories) {
this.enabledCategories.clear();
categories.forEach(cat => this.enabledCategories.add(cat));
}
debug(message, data = {}, category = 'general') {
this.log('DEBUG', message, data, category);
}
info(message, data = {}, category = 'general') {
this.log('INFO', message, data, category);
}
warn(message, data = {}, category = 'general') {
this.log('WARN', message, data, category);
}
error(message, data = {}, category = 'general') {
this.log('ERROR', message, data, category);
}
critical(message, data = {}, category = 'general') {
this.log('CRITICAL', message, data, category);
// 严重错误立即上报
this.flushBuffer();
}
log(level, message, data = {}, category = 'general') {
const levelNum = this.logLevels[level];
// 检查日志级别
if (levelNum < this.currentLevel) return;
// 检查类别
if (!this.enabledCategories.has('all') && !this.enabledCategories.has(category)) {
return;
}
const logEntry = {
timestamp: Date.now(),
level,
message,
data: this.sanitizeData(data),
category,
url: window.location.href,
userAgent: navigator.userAgent,
userId: window.APP_CONFIG?.userId,
sessionId: window.APP_CONFIG?.sessionId,
stack: level === 'ERROR' || level === 'CRITICAL' ? new Error().stack : null
};
// 添加到本地日志
this.logs.push(logEntry);
// 限制日志数量
if (this.logs.length > this.maxLogs) {
this.logs.shift();
}
// 添加到上报缓冲区
this.logBuffer.push(logEntry);
// 控制台输出
this.outputToConsole(logEntry);
// 触发日志事件
EventBus.emit('log-entry', logEntry);
// 严重错误立即处理
if (level === 'CRITICAL') {
this.handleCriticalLog(logEntry);
}
}
sanitizeData(data) {
// 深拷贝并移除敏感信息
const sanitized = JSON.parse(JSON.stringify(data, (key, value) => {
// 移除敏感字段
const sensitiveFields = ['password', 'token', 'secret', 'key', 'auth'];
if (sensitiveFields.some(field => key.toLowerCase().includes(field))) {
return '[REDACTED]';
}
// 限制字符串长度
if (typeof value === 'string' && value.length > 1000) {
return value.substring(0, 1000) + '...[TRUNCATED]';
}
return value;
}));
return sanitized;
}
outputToConsole(logEntry) {
const { level, message, data } = logEntry;
const style = this.getConsoleStyle(level);
if (Object.keys(data).length > 0) {
console.log(
`%c[${level}] ${message}`,
style,
data
);
} else {
console.log(
`%c[${level}] ${message}`,
style
);
}
}
getConsoleStyle(level) {
const styles = {
DEBUG: 'color: #666; font-size: 11px;',
INFO: 'color: #0066cc; font-weight: normal;',
WARN: 'color: #ff9900; font-weight: bold;',
ERROR: 'color: #cc0000; font-weight: bold;',
CRITICAL: 'color: #ffffff; background-color: #cc0000; font-weight: bold; padding: 2px 4px;'
};
return styles[level] || styles.INFO;
}
interceptConsole() {
const originalConsole = {
log: console.log,
warn: console.warn,
error: console.error,
info: console.info,
debug: console.debug
};
// 拦截console.error
console.error = (...args) => {
originalConsole.error(...args);
this.error('Console Error', { args: args.map(this.stringifyArg) }, 'console');
};
// 拦截console.warn
console.warn = (...args) => {
originalConsole.warn(...args);
this.warn('Console Warning', { args: args.map(this.stringifyArg) }, 'console');
};
// 保存原始方法引用
this.originalConsole = originalConsole;
}
stringifyArg(arg) {
if (typeof arg === 'string') return arg;
if (arg instanceof Error) return { name: arg.name, message: arg.message, stack: arg.stack };
try {
return JSON.stringify(arg);
} catch {
return String(arg);
}
}
setupAutoFlush() {
setInterval(() => {
if (this.logBuffer.length > 0) {
this.flushBuffer();
}
}, this.bufferFlushInterval);
// 页面卸载时立即刷新
window.addEventListener('beforeunload', () => {
this.flushBuffer();
});
}
async flushBuffer() {
if (this.logBuffer.length === 0) return;
const logsToSend = [...this.logBuffer];
this.logBuffer = [];
try {
await fetch('/api/logging/batch', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
logs: logsToSend,
metadata: {
timestamp: Date.now(),
source: 'frontend',
version: window.APP_CONFIG?.version
}
})
});
} catch (error) {
// 发送失败,重新加入缓冲区
this.logBuffer.unshift(...logsToSend);
console.warn('日志上报失败:', error);
}
}
handleCriticalLog(logEntry) {
// 立即通知运维
this.sendCriticalAlert(logEntry);
// 尝试收集更多上下文信息
const context = this.collectCriticalContext();
// 发送详细错误报告
this.sendDetailedErrorReport(logEntry, context);
}
async sendCriticalAlert(logEntry) {
try {
await fetch('/api/alerts/critical', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
type: 'critical-frontend-error',
message: logEntry.message,
data: logEntry.data,
timestamp: logEntry.timestamp,
url: logEntry.url,
userId: logEntry.userId
})
});
} catch (error) {
console.error('严重错误告警发送失败:', error);
}
}
collectCriticalContext() {
return {
performance: PerformanceMonitor?.getPerformanceScore() || 'unknown',
memory: performance.memory ? {
used: Math.round(performance.memory.usedJSHeapSize / 1024 / 1024),
total: Math.round(performance.memory.totalJSHeapSize / 1024 / 1024)
} : 'unknown',
recentLogs: this.logs.slice(-20), // 最近20条日志
userActions: this.getUserActions(), // 用户最近操作
systemState: this.getSystemState()
};
}
getUserActions() {
// 从事件总线获取最近的用户操作
return EventBus.getRecentEvents?.('user-action') || [];
}
getSystemState() {
return {
activeComponents: Object.keys(window.components || {}),
cacheStats: CacheSystem?.getStats?.() || {},
errorHistory: ErrorRecovery?.getErrorStatistics?.() || {}
};
}
async sendDetailedErrorReport(logEntry, context) {
try {
await fetch('/api/errors/detailed-report', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
error: logEntry,
context,
timestamp: Date.now()
})
});
} catch (error) {
console.error('详细错误报告发送失败:', error);
}
}
// 搜索日志
searchLogs(query, options = {}) {
const {
level = null,
category = null,
timeRange = null,
limit = 100
} = options;
let filteredLogs = this.logs;
// 按级别过滤
if (level) {
filteredLogs = filteredLogs.filter(log => log.level === level);
}
// 按类别过滤
if (category) {
filteredLogs = filteredLogs.filter(log => log.category === category);
}
// 按时间范围过滤
if (timeRange) {
const now = Date.now();
const startTime = now - timeRange;
filteredLogs = filteredLogs.filter(log => log.timestamp >= startTime);
}
// 按查询字符串过滤
if (query) {
const queryLower = query.toLowerCase();
filteredLogs = filteredLogs.filter(log =>
log.message.toLowerCase().includes(queryLower) ||
JSON.stringify(log.data).toLowerCase().includes(queryLower)
);
}
// 限制结果数量
return filteredLogs.slice(-limit);
}
// 导出日志
exportLogs(format = 'json') {
const data = format === 'csv' ? this.convertToCSV() : JSON.stringify(this.logs, null, 2);
const blob = new Blob([data], {
type: format === 'csv' ? 'text/csv' : 'application/json'
});
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = `logs-${Date.now()}.${format}`;
link.click();
URL.revokeObjectURL(url);
}
convertToCSV() {
const headers = ['timestamp', 'level', 'category', 'message', 'data'];
const rows = this.logs.map(log => [
new Date(log.timestamp).toISOString(),
log.level,
log.category,
log.message,
JSON.stringify(log.data)
]);
return [headers, ...rows]
.map(row => row.map(field => `"${field}"`).join(','))
.join('\n');
}
}
// 全局日志系统
window.Logger = new IntelligentLogger();
2.3.2 自动化部署优化
A. 前端构建优化
// 构建配置优化 (webpack.config.js 概念实现)
const OptimizedBuildConfig = {
// 代码分割策略
splitChunks: {
chunks: 'all',
cacheGroups: {
// 第三方库单独打包
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
priority: 10
},
// 公共组件单独打包
common: {
name: 'common',
minChunks: 2,
chunks: 'all',
priority: 5,
reuseExistingChunk: true
},
// 图表组件单独打包
charts: {
test: /[\\/]components[\\/]charts[\\/]/,
name: 'charts',
chunks: 'all',
priority: 8
}
}
},
// 优化配置
optimization: {
// 生产环境压缩
minimize: true,
minimizer: [
// JavaScript压缩
{
terserOptions: {
compress: {
drop_console: true, // 移除console
drop_debugger: true, // 移除debugger
pure_funcs: ['console.log'] // 移除特定函数
},
mangle: {
safari10: true
}
}
},
// CSS压缩
{
cssProcessorOptions: {
map: {
inline: false,
annotation: true
}
}
}
],
// Tree Shaking
usedExports: true,
sideEffects: false
},
// 输出配置
output: {
// 文件名模板
filename: '[name].[contenthash:8].js',
chunkFilename: '[name].[contenthash:8].chunk.js',
// 公共路径
publicPath: '/static/',
// 清理输出目录
clean: true
},
// 模块解析
resolve: {
// 别名配置
alias: {
'@': '/src',
'@components': '/src/components',
'@utils': '/src/utils',
'@api': '/src/api'
},
// 扩展名
extensions: ['.js', '.jsx', '.ts', '.tsx', '.css', '.scss']
},
// 加载器配置
module: {
rules: [
// JavaScript/TypeScript
{
test: /\.(js|jsx|ts|tsx)$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', {
modules: false,
useBuiltIns: 'usage',
corejs: 3
}]
],
plugins: [
'@babel/plugin-syntax-dynamic-import',
'@babel/plugin-proposal-class-properties'
]
}
}
]
},
// CSS
{
test: /\.css$/,
use: [
'style-loader',
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[name]__[local]--[hash:base64:5]'
}
}
},
'postcss-loader'
]
},
// 图片
{
test: /\.(png|jpg|jpeg|gif|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 8KB以下内联
}
},
generator: {
filename: 'images/[name].[hash:8][ext]'
}
},
// 字体
{
test: /\.(woff|woff2|eot|ttf|otf)$/,
type: 'asset/resource',
generator: {
filename: 'fonts/[name].[hash:8][ext]'
}
}
]
},
// 插件配置
plugins: [
// HTML模板
{
name: 'HtmlWebpackPlugin',
options: {
template: './src/index.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true
}
}
},
// 提取CSS
{
name: 'MiniCssExtractPlugin',
options: {
filename: '[name].[contenthash:8].css',
chunkFilename: '[name].[contenthash:8].chunk.css'
}
},
// 分析包大小
{
name: 'BundleAnalyzerPlugin',
options: {
analyzerMode: 'static',
openAnalyzer: false,
reportFilename: 'bundle-report.html'
}
},
// 复制静态文件
{
name: 'CopyWebpackPlugin',
patterns: [
{
from: 'public',
to: '.',
globOptions: {
ignore: ['**/index.html']
}
}
]
}
],
// 开发服务器
devServer: {
port: 3000,
hot: true,
historyApiFallback: true,
proxy: {
'/api': {
target: 'http://localhost:5225',
changeOrigin: true
}
}
}
};
B. CI/CD 优化流程
# .github/workflows/deploy.yml
name: 前端优化部署流程
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
NODE_VERSION: '18'
PYTHON_VERSION: '3.11'
jobs:
# 代码质量检查
quality-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Lint检查
run: npm run lint
- name: 类型检查
run: npm run type-check
- name: 单元测试
run: npm run test:unit
- name: 代码覆盖率
run: npm run test:coverage
- name: 上传覆盖率报告
uses: codecov/codecov-action@v3
# 构建优化
build:
needs: quality-check
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: 生成版本号
id: version
run: |
VERSION=$(date +'%Y%m%d')-${GITHUB_SHA::8}
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: 构建生产版本
run: |
npm run build
npm run optimize
env:
NODE_ENV: production
BUILD_VERSION: ${{ env.VERSION }}
- name: 压缩静态资源
run: |
find dist -name "*.js" -exec gzip -k {} \;
find dist -name "*.css" -exec gzip -k {} \;
find dist -name "*.html" -exec gzip -k {} \;
- name: 分析构建产物
run: |
npm run analyze
ls -la dist/
- name: 上传构建产物
uses: actions/upload-artifact@v3
with:
name: build-artifacts-${{ env.VERSION }}
path: |
dist/
docs/bundle-report.html
retention-days: 30
# 性能测试
performance-test:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: 下载构建产物
uses: actions/download-artifact@v3
with:
name: build-artifacts-${{ needs.build.outputs.version }}
path: ./dist
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: 启动后端服务
run: |
cd ljwx-bigscreen
pip install -r requirements.txt
python run.py &
sleep 10
- name: 运行Lighthouse性能测试
run: |
npm install -g @lhci/cli
lhci autorun --upload.target=temporary-public-storage
env:
LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_APP_TOKEN }}
- name: 运行负载测试
run: |
npm run test:load
- name: 生成性能报告
run: |
npm run performance:report
# 部署到测试环境
deploy-staging:
needs: [build, performance-test]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v3
- name: 下载构建产物
uses: actions/download-artifact@v3
with:
name: build-artifacts-${{ needs.build.outputs.version }}
path: ./dist
- name: 部署到测试环境
run: |
# 使用rsync或其他工具部署
rsync -avz --delete dist/ staging-server:/var/www/personal-dashboard/
# 更新nginx配置
ssh staging-server 'sudo nginx -t && sudo systemctl reload nginx'
# 清理CDN缓存
curl -X POST "https://api.cdn.com/purge" \
-H "Authorization: Bearer ${{ secrets.CDN_TOKEN }}" \
-d '{"files": ["/*"]}'
# 部署到生产环境
deploy-production:
needs: [build, performance-test]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: production
steps:
- uses: actions/checkout@v3
- name: 下载构建产物
uses: actions/download-artifact@v3
with:
name: build-artifacts-${{ needs.build.outputs.version }}
path: ./dist
- name: 生产环境预检查
run: |
# 检查构建产物完整性
npm run verify:build
# 检查资源大小
npm run check:bundle-size
- name: 蓝绿部署
run: |
# 部署到新环境
./scripts/blue-green-deploy.sh
env:
DEPLOY_VERSION: ${{ needs.build.outputs.version }}
- name: 健康检查
run: |
# 等待服务启动
sleep 30
# 健康检查
curl -f http://production-server/health || exit 1
# 性能检查
npm run health:performance
- name: 切换流量
run: |
# 切换负载均衡器流量
./scripts/switch-traffic.sh
- name: 清理旧版本
run: |
# 保留最近3个版本
./scripts/cleanup-old-versions.sh 3
# 监控和告警
post-deploy:
needs: [deploy-production]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: 发送部署通知
run: |
curl -X POST "https://hooks.slack.com/services/${{ secrets.SLACK_WEBHOOK }}" \
-H "Content-Type: application/json" \
-d '{
"text": "✅ Personal.html优化版本部署成功",
"attachments": [{
"color": "good",
"fields": [
{"title": "版本", "value": "${{ needs.build.outputs.version }}", "short": true},
{"title": "提交", "value": "'${GITHUB_SHA::8}'", "short": true},
{"title": "环境", "value": "生产环境", "short": true}
]
}]
}'
- name: 启动监控
run: |
# 启动特殊监控任务
curl -X POST "https://monitoring-api.com/start-enhanced-monitoring" \
-H "Authorization: Bearer ${{ secrets.MONITORING_TOKEN }}" \
-d '{"version": "${{ needs.build.outputs.version }}"}'
3. 实施时间线与里程碑
第一阶段:基础架构重构 (周1-3)
- 周1: 文件结构设计与基础模板创建
- 周2: 组件系统架构实现
- 周3: API客户端与缓存系统开发
第二阶段:性能优化核心 (周4-6)
- 周4: 资源加载优化与懒加载实现
- 周5: 内存管理与错误恢复系统
- 周6: 数据更新策略优化
第三阶段:用户体验提升 (周7-9)
- 周7: 响应式设计升级
- 周8: 主题系统与交互优化
- 周9: 加载状态与错误处理完善
第四阶段:运维体系建设 (周10-12)
- 周10: 监控系统部署
- 周11: 日志系统与自动化测试
- 周12: CI/CD优化与上线
4. 预期效果与评估指标
4.1 性能指标
const PerformanceTargets = {
// 加载性能
firstContentfulPaint: { current: '2.8s', target: '1.2s', improvement: '57%' },
largestContentfulPaint: { current: '4.5s', target: '2.0s', improvement: '56%' },
timeToInteractive: { current: '6.2s', target: '2.8s', improvement: '55%' },
// 运行时性能
avgApiResponse: { current: '850ms', target: '350ms', improvement: '59%' },
memoryUsage: { current: '78MB', target: '45MB', improvement: '42%' },
frameRate: { current: '45fps', target: '58fps', improvement: '29%' },
// 用户体验
bounceRate: { current: '23%', target: '12%', improvement: '48%' },
pageLoadSuccess: { current: '94%', target: '99.5%', improvement: '5.9%' },
errorRate: { current: '2.3%', target: '0.5%', improvement: '78%' }
};
4.2 代码质量指标
const QualityMetrics = {
// 可维护性
codeComplexity: { current: 'High', target: 'Low', improvement: '70%' },
testCoverage: { current: '35%', target: '85%', improvement: '143%' },
duplicationRate: { current: '18%', target: '5%', improvement: '72%' },
// 开发效率
buildTime: { current: '3.2min', target: '1.1min', improvement: '66%' },
hotReloadTime: { current: '8s', target: '2s', improvement: '75%' },
deploymentTime: { current: '12min', target: '4min', improvement: '67%' }
};
5. 风险评估与应对策略
5.1 技术风险
- 兼容性风险: 通过渐进式增强和polyfill确保向后兼容
- 性能回归风险: 建立持续的性能监控和自动化测试
- 数据丢失风险: 实现完善的缓存回退机制和错误恢复
5.2 项目风险
- 时间风险: 采用MVP方式,优先实现核心功能
- 资源风险: 分阶段实施,允许并行开发
- 用户体验风险: 保持功能对等,提供平滑的迁移路径
6. 总结与建议
6.1 核心优势
- 系统性优化: 从架构到实现的全链路优化
- 可持续发展: 建立长期可维护的代码结构
- 性能提升: 多维度性能优化,显著提升用户体验
- 运维友好: 完善的监控、日志和自动化体系
6.2 实施建议
- 分阶段执行: 避免一次性替换带来的风险
- 充分测试: 建立完善的测试体系确保质量
- 监控先行: 优先建立监控体系,确保问题及时发现
- 文档同步: 保持文档与代码同步更新
6.3 成功关键因素
- 团队协作: 建立清晰的分工和沟通机制
- 质量控制: 严格的代码审查和测试标准
- 持续优化: 基于监控数据的持续改进
- 用户反馈: 积极收集和响应用户反馈
通过这套全面的优化方案,personal.html将从一个9,792行的巨型文件转变为现代化、高性能、易维护的Web应用系统,不仅解决当前的技术债务,更为未来的功能扩展和系统演进奠定坚实基础。
文档版本: v1.0
制定日期: 2025-09-18
更新日期: 2025-09-18
审核状态: 待审核
实施优先级: 高