Files
Agent-n8n/web_frontend/web_result/js/performance-optimizer.js
Yep_Q 3825deb3e0 优化项目配置和页面结构
- 更新settings.local.json,移除冗余的权限设置,添加新的Playwright相关权限。
- 删除exhibition_demo_project_2025.md文档,清理不再使用的文件。
- 更新多个HTML页面,统一viewport设置,添加页面加载动画、错误处理和性能优化脚本。
- 统一使用Tailwind CSS的引入方式,提升页面加载性能。
- 增强导航组件,支持移动端菜单和返回顶部功能,改善用户体验。
2025-09-10 02:35:16 +08:00

461 lines
16 KiB
JavaScript

// 页面性能优化组件
(function() {
let performanceData = {};
let isOptimizing = false;
// 性能监测
function measurePerformance() {
if ('performance' in window) {
const navigation = performance.getEntriesByType('navigation')[0];
const paint = performance.getEntriesByType('paint');
performanceData = {
// 页面加载时间
domLoading: navigation.domContentLoadedEventEnd - navigation.navigationStart,
pageLoading: navigation.loadEventEnd - navigation.navigationStart,
// 渲染时间
firstPaint: paint.find(p => p.name === 'first-paint')?.startTime || 0,
firstContentfulPaint: paint.find(p => p.name === 'first-contentful-paint')?.startTime || 0,
// 网络时间
dnsTime: navigation.domainLookupEnd - navigation.domainLookupStart,
connectTime: navigation.connectEnd - navigation.connectStart,
requestTime: navigation.responseStart - navigation.requestStart,
downloadTime: navigation.responseEnd - navigation.responseStart,
// 内存使用(如果支持)
memory: performance.memory ? {
used: Math.round(performance.memory.usedJSHeapSize / 1024 / 1024),
total: Math.round(performance.memory.totalJSHeapSize / 1024 / 1024),
limit: Math.round(performance.memory.jsHeapSizeLimit / 1024 / 1024)
} : null
};
}
return performanceData;
}
// 资源预加载优化
function optimizeResourceLoading() {
// 根据当前页面位置调整路径
const isInPagesFolder = window.location.pathname.includes('/pages/');
const basePath = isInPagesFolder ? '../' : '';
// 预加载关键资源
const criticalResources = [
{ href: basePath + 'css/styles.css', as: 'style' },
{ href: basePath + 'css/animations.css', as: 'style' },
{ href: basePath + 'js/nav-component.js', as: 'script' }
];
criticalResources.forEach(resource => {
if (!document.querySelector(`link[href*="${resource.href.split('/').pop()}"]`)) {
const link = document.createElement('link');
link.rel = 'preload';
link.href = resource.href;
link.as = resource.as;
document.head.appendChild(link);
}
});
}
// 图片懒加载和优化
function optimizeImages() {
// 创建 Intersection Observer 用于懒加载
if ('IntersectionObserver' in window) {
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
// 懒加载图片
if (img.dataset.src) {
img.src = img.dataset.src;
img.removeAttribute('data-src');
}
// 添加加载状态
img.classList.add('loading');
img.addEventListener('load', () => {
img.classList.remove('loading');
img.classList.add('loaded');
});
img.addEventListener('error', () => {
img.classList.add('error');
img.style.display = 'none';
});
observer.unobserve(img);
}
});
}, {
rootMargin: '50px',
threshold: 0.01
});
// 观察所有图片
document.querySelectorAll('img[data-src], img:not([src])').forEach(img => {
imageObserver.observe(img);
});
}
// 为现有图片添加优化
document.querySelectorAll('img').forEach(img => {
// 添加 loading="lazy" 属性
if (!img.loading) {
img.loading = 'lazy';
}
// 添加 decode="async" 属性
if (!img.decode) {
img.decode = 'async';
}
// 图片错误处理
img.addEventListener('error', function() {
this.style.opacity = '0';
this.style.transition = 'opacity 0.3s';
});
});
}
// CSS 优化
function optimizeCSS() {
// 内联关键 CSS
const criticalCSS = `
/* 关键渲染路径 CSS */
body { font-family: 'Inter', sans-serif; margin: 0; padding: 0; }
#navbar { position: fixed; top: 0; width: 100%; z-index: 50; background: white; }
.container { max-width: 1200px; margin: 0 auto; padding: 0 1rem; }
/* 页面加载器样式 */
#page-loader { position: fixed; inset: 0; z-index: 9999; background: white; display: flex; align-items: center; justify-content: center; }
`;
// 添加关键 CSS 到 head
if (!document.getElementById('critical-css')) {
const style = document.createElement('style');
style.id = 'critical-css';
style.textContent = criticalCSS;
document.head.insertBefore(style, document.head.firstChild);
}
// 异步加载非关键 CSS
const nonCriticalCSS = document.querySelectorAll('link[rel="stylesheet"]:not([data-critical])');
nonCriticalCSS.forEach(link => {
if (link.href.includes('tailwind') || link.href.includes('font-awesome')) {
link.media = 'print';
link.addEventListener('load', function() {
this.media = 'all';
});
}
});
}
// JavaScript 优化
function optimizeJavaScript() {
// 延迟加载非关键脚本
const deferScripts = [
'js/back-to-top.js',
'js/mobile-optimize.js'
];
deferScripts.forEach(src => {
const script = document.querySelector(`script[src*="${src}"]`);
if (script && !script.defer && !script.async) {
script.defer = true;
}
});
// 移除未使用的事件监听器(防止内存泄漏)
const cleanupEvents = () => {
// 移除已销毁元素的事件监听器
document.querySelectorAll('[data-cleanup]').forEach(el => {
el.removeEventListener('click', null);
el.removeEventListener('scroll', null);
el.removeEventListener('resize', null);
});
};
// 页面卸载时清理
window.addEventListener('beforeunload', cleanupEvents);
}
// 网络优化
function optimizeNetwork() {
// DNS 预解析
const domains = [
'fonts.googleapis.com',
'cdnjs.cloudflare.com',
'cdn.jsdelivr.net'
];
domains.forEach(domain => {
if (!document.querySelector(`link[rel="dns-prefetch"][href*="${domain}"]`)) {
const link = document.createElement('link');
link.rel = 'dns-prefetch';
link.href = `https://${domain}`;
document.head.appendChild(link);
}
});
// 预连接到关键域名
const preconnectDomains = ['fonts.gstatic.com'];
preconnectDomains.forEach(domain => {
if (!document.querySelector(`link[rel="preconnect"][href*="${domain}"]`)) {
const link = document.createElement('link');
link.rel = 'preconnect';
link.href = `https://${domain}`;
link.crossOrigin = '';
document.head.appendChild(link);
}
});
}
// 缓存优化
function optimizeCache() {
// Service Worker 注册(如果支持)
if ('serviceWorker' in navigator) {
window.addEventListener('load', () => {
navigator.serviceWorker.register('./sw.js')
.then(registration => {
console.log('SW registered: ', registration);
})
.catch(registrationError => {
console.log('SW registration failed: ', registrationError);
});
});
}
// 本地存储优化
try {
// 缓存静态数据
if (localStorage) {
const cacheKey = 'page-cache-' + window.location.pathname;
const cacheTime = 30 * 60 * 1000; // 30分钟
const cachedData = localStorage.getItem(cacheKey);
if (cachedData) {
const { timestamp, data } = JSON.parse(cachedData);
if (Date.now() - timestamp < cacheTime) {
// 使用缓存数据
console.log('Using cached data');
}
}
}
} catch (e) {
console.warn('LocalStorage not available');
}
}
// 动画性能优化
function optimizeAnimations() {
// 检测是否需要减少动画
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (prefersReducedMotion) {
document.body.classList.add('reduce-motion');
// 添加减少动画的 CSS
const style = document.createElement('style');
style.textContent = `
.reduce-motion *,
.reduce-motion *::before,
.reduce-motion *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
`;
document.head.appendChild(style);
}
// 使用 transform 和 opacity 进行动画(硬件加速)
document.querySelectorAll('[class*="animate-"]').forEach(el => {
el.style.willChange = 'transform, opacity';
// 动画完成后移除 will-change
el.addEventListener('animationend', function() {
this.style.willChange = 'auto';
}, { once: true });
});
}
// 内存优化
function optimizeMemory() {
// 定期清理无用的 DOM 元素
const cleanup = () => {
// 移除隐藏的元素(如果不再需要)
document.querySelectorAll('.hidden, [style*="display: none"]').forEach(el => {
if (el.dataset.keepHidden !== 'true') {
// 这里可以添加更智能的清理逻辑
}
});
};
// 每5分钟执行一次清理
setInterval(cleanup, 5 * 60 * 1000);
// 监听内存压力(如果支持)
if ('memory' in performance) {
const checkMemory = () => {
const used = performance.memory.usedJSHeapSize;
const limit = performance.memory.jsHeapSizeLimit;
const usage = used / limit;
if (usage > 0.8) {
console.warn('High memory usage detected:', Math.round(usage * 100) + '%');
cleanup();
}
};
setInterval(checkMemory, 30000); // 每30秒检查一次
}
}
// 性能监控和报告
function monitorPerformance() {
// 使用 Performance Observer 监控
if ('PerformanceObserver' in window) {
// 监控 Long Tasks
const longTaskObserver = new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
if (entry.duration > 50) {
console.warn('Long task detected:', entry.duration + 'ms');
}
});
});
try {
longTaskObserver.observe({ entryTypes: ['longtask'] });
} catch (e) {
// longtask 不被支持
}
// 监控 Layout Shift
const clsObserver = new PerformanceObserver(list => {
list.getEntries().forEach(entry => {
if (entry.value > 0.1) {
console.warn('Layout shift detected:', entry.value);
}
});
});
try {
clsObserver.observe({ entryTypes: ['layout-shift'] });
} catch (e) {
// layout-shift 不被支持
}
}
}
// 添加性能优化样式
function addPerformanceStyles() {
const style = document.createElement('style');
style.textContent = `
/* 硬件加速 */
.gpu-accelerated {
transform: translateZ(0);
will-change: transform, opacity;
}
/* 图片加载状态 */
img.loading {
opacity: 0.3;
filter: blur(5px);
transition: opacity 0.3s, filter 0.3s;
}
img.loaded {
opacity: 1;
filter: none;
}
img.error {
opacity: 0;
display: none !important;
}
/* 字体加载优化 */
.font-loading {
font-display: swap;
}
/* 减少重绘 */
.contain-layout {
contain: layout;
}
.contain-paint {
contain: paint;
}
/* 滚动优化 */
.scroll-smooth {
scroll-behavior: smooth;
-webkit-overflow-scrolling: touch;
}
`;
document.head.appendChild(style);
}
// 初始化性能优化
function init() {
if (isOptimizing) return;
isOptimizing = true;
// 立即执行的优化
optimizeResourceLoading();
optimizeCSS();
optimizeNetwork();
addPerformanceStyles();
// DOM 加载完成后执行
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
optimizeImages();
optimizeJavaScript();
optimizeAnimations();
monitorPerformance();
});
} else {
optimizeImages();
optimizeJavaScript();
optimizeAnimations();
monitorPerformance();
}
// 页面完全加载后执行
window.addEventListener('load', () => {
optimizeCache();
optimizeMemory();
// 测量性能
setTimeout(() => {
const perf = measurePerformance();
console.log('Performance metrics:', perf);
// 发送性能数据到分析系统(如果需要)
if (window.gtag) {
gtag('event', 'performance', {
'page_load_time': Math.round(perf.pageLoading),
'dom_load_time': Math.round(perf.domLoading)
});
}
}, 1000);
});
}
// 提供外部接口
window.PerformanceOptimizer = {
measurePerformance,
optimizeImages,
getPerformanceData: () => performanceData
};
// 初始化
init();
})();