360 lines
12 KiB
TypeScript
360 lines
12 KiB
TypeScript
// 性能优化配置
|
||
export const performanceConfig = {
|
||
// 缓存配置
|
||
cache: {
|
||
// API 缓存时间 (秒)
|
||
apiCacheDuration: 3600, // 1小时
|
||
// ISR 重新验证时间 (秒)
|
||
isrRevalidate: 3600, // 1小时
|
||
// 静态资源缓存时间
|
||
staticAssetsCacheDuration: 86400, // 24小时
|
||
},
|
||
|
||
// 预加载配置
|
||
prefetch: {
|
||
// 关键页面列表
|
||
criticalPages: ['/products', '/news', '/contact', '/support', '/about'],
|
||
// 预加载延迟 (毫秒)
|
||
prefetchDelay: 100,
|
||
// 关键图片列表
|
||
criticalImages: [
|
||
'/images/hero-bg.jpg',
|
||
'/images/cloud-services.jpg',
|
||
'/images/aws-logo.png',
|
||
'/images/server-room.jpg',
|
||
],
|
||
},
|
||
|
||
// 图片优化配置
|
||
images: {
|
||
// 图片质量
|
||
quality: 85,
|
||
// 响应式图片断点
|
||
breakpoints: [640, 768, 1024, 1280, 1536],
|
||
// 图片格式优先级
|
||
formats: ['webp', 'avif', 'jpg', 'png'],
|
||
// 懒加载距离
|
||
lazyLoadRootMargin: '50px',
|
||
},
|
||
|
||
// 代码分割配置
|
||
codeSplitting: {
|
||
// 重要组件预加载
|
||
importantComponents: [
|
||
'Header',
|
||
'Footer',
|
||
'NewsArticleServerComponent',
|
||
'ProductsServerComponent',
|
||
],
|
||
// 延迟加载的组件
|
||
lazyComponents: [
|
||
'ContactForm',
|
||
'NewsletterSubscribe',
|
||
'ChatWidget',
|
||
],
|
||
},
|
||
|
||
// 性能监控配置
|
||
monitoring: {
|
||
// Core Web Vitals 阈值
|
||
coreWebVitals: {
|
||
lcp: 2500, // Largest Contentful Paint (ms)
|
||
fid: 100, // First Input Delay (ms)
|
||
cls: 0.1, // Cumulative Layout Shift
|
||
},
|
||
// 启用性能监控
|
||
enabled: process.env.NODE_ENV === 'production',
|
||
// 性能数据上报URL
|
||
reportUrl: process.env.NEXT_PUBLIC_PERFORMANCE_API,
|
||
},
|
||
|
||
// CDN 配置
|
||
cdn: {
|
||
// 静态资源CDN域名
|
||
domain: process.env.NEXT_PUBLIC_CDN_DOMAIN || '',
|
||
// 图片CDN域名
|
||
imageDomain: process.env.NEXT_PUBLIC_IMAGE_CDN_DOMAIN || '',
|
||
// 启用CDN
|
||
enabled: process.env.NODE_ENV === 'production',
|
||
},
|
||
|
||
// 压缩配置
|
||
compression: {
|
||
// 启用gzip压缩
|
||
gzip: true,
|
||
// 启用brotli压缩
|
||
brotli: true,
|
||
// 压缩级别
|
||
level: 6,
|
||
// 最小压缩文件大小
|
||
threshold: 1024,
|
||
},
|
||
};
|
||
|
||
// 性能优化工具函数
|
||
export class PerformanceOptimizer {
|
||
// 预加载关键资源
|
||
static preloadCriticalResources() {
|
||
if (typeof window === 'undefined') return;
|
||
|
||
// 预加载关键图片
|
||
performanceConfig.prefetch.criticalImages.forEach(src => {
|
||
// 检查是否已经存在相同的预加载链接
|
||
if (!document.querySelector(`link[rel="preload"][as="image"][href="${src}"]`)) {
|
||
const link = document.createElement('link');
|
||
link.rel = 'preload';
|
||
link.as = 'image';
|
||
link.href = src;
|
||
document.head.appendChild(link);
|
||
}
|
||
});
|
||
|
||
// 预加载关键字体
|
||
const criticalFonts = [
|
||
'/fonts/inter-var.woff2',
|
||
];
|
||
|
||
criticalFonts.forEach(href => {
|
||
// 检查是否已经存在相同的预加载链接
|
||
if (!document.querySelector(`link[rel="preload"][as="font"][href="${href}"]`)) {
|
||
const link = document.createElement('link');
|
||
link.rel = 'preload';
|
||
link.as = 'font';
|
||
link.type = 'font/woff2';
|
||
link.crossOrigin = 'anonymous';
|
||
link.href = href;
|
||
document.head.appendChild(link);
|
||
}
|
||
});
|
||
}
|
||
|
||
// 监控页面性能
|
||
static monitorPerformance() {
|
||
if (typeof window === 'undefined' || !performanceConfig.monitoring.enabled) return;
|
||
|
||
// 监控Core Web Vitals (需要安装 web-vitals 包)
|
||
try {
|
||
// 动态导入web-vitals以避免类型错误
|
||
const loadWebVitals = async () => {
|
||
try {
|
||
const webVitals = await import('web-vitals');
|
||
webVitals.onCLS(this.sendToAnalytics);
|
||
// onFID已被onINP替代
|
||
if ('onINP' in webVitals) {
|
||
(webVitals as any).onINP(this.sendToAnalytics);
|
||
} else if ('onFID' in webVitals) {
|
||
(webVitals as any).onFID(this.sendToAnalytics);
|
||
}
|
||
webVitals.onLCP(this.sendToAnalytics);
|
||
} catch (err) {
|
||
console.log('web-vitals not available');
|
||
}
|
||
};
|
||
loadWebVitals();
|
||
} catch (error) {
|
||
console.log('web-vitals not available');
|
||
}
|
||
|
||
// 监控导航时间
|
||
window.addEventListener('load', () => {
|
||
setTimeout(() => {
|
||
const navigationTiming = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;
|
||
if (navigationTiming) {
|
||
const metrics = {
|
||
dns: navigationTiming.domainLookupEnd - navigationTiming.domainLookupStart,
|
||
tcp: navigationTiming.connectEnd - navigationTiming.connectStart,
|
||
request: navigationTiming.responseStart - navigationTiming.requestStart,
|
||
response: navigationTiming.responseEnd - navigationTiming.responseStart,
|
||
dom: navigationTiming.domContentLoadedEventEnd - navigationTiming.domContentLoadedEventStart,
|
||
load: navigationTiming.loadEventEnd - navigationTiming.loadEventStart,
|
||
total: navigationTiming.loadEventEnd - navigationTiming.fetchStart,
|
||
};
|
||
|
||
console.log('页面性能指标:', metrics);
|
||
this.sendPerformanceData(metrics);
|
||
}
|
||
}, 0);
|
||
});
|
||
}
|
||
|
||
// 发送性能数据到分析服务
|
||
static sendToAnalytics(metric: any) {
|
||
if (performanceConfig.monitoring.reportUrl) {
|
||
fetch(performanceConfig.monitoring.reportUrl, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify(metric),
|
||
}).catch(err => console.warn('性能数据上报失败:', err));
|
||
}
|
||
}
|
||
|
||
// 发送性能数据
|
||
static sendPerformanceData(data: any) {
|
||
if (performanceConfig.monitoring.reportUrl) {
|
||
fetch(performanceConfig.monitoring.reportUrl, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ type: 'navigation', data }),
|
||
}).catch(err => console.warn('性能数据上报失败:', err));
|
||
}
|
||
}
|
||
|
||
// 优化图片加载
|
||
static optimizeImageLoading() {
|
||
if (typeof window === 'undefined') return;
|
||
|
||
// 创建Intersection Observer监听图片懒加载
|
||
const imageObserver = new IntersectionObserver(
|
||
(entries) => {
|
||
entries.forEach(entry => {
|
||
if (entry.isIntersecting) {
|
||
const img = entry.target as HTMLImageElement;
|
||
if (img.dataset.src) {
|
||
img.src = img.dataset.src;
|
||
img.removeAttribute('data-src');
|
||
imageObserver.unobserve(img);
|
||
}
|
||
}
|
||
});
|
||
},
|
||
{
|
||
rootMargin: performanceConfig.images.lazyLoadRootMargin,
|
||
}
|
||
);
|
||
|
||
// 观察所有延迟加载的图片
|
||
document.querySelectorAll('img[data-src]').forEach(img => {
|
||
imageObserver.observe(img);
|
||
});
|
||
|
||
// 在页面卸载时断开Observer
|
||
const cleanup = () => {
|
||
imageObserver.disconnect();
|
||
};
|
||
|
||
// 监听页面卸载事件
|
||
window.addEventListener('beforeunload', cleanup);
|
||
|
||
// 返回清理函数以便手动调用
|
||
return cleanup;
|
||
}
|
||
|
||
// 预热缓存
|
||
static warmupCache(urls: string[]) {
|
||
if (typeof window === 'undefined') return;
|
||
|
||
urls.forEach(url => {
|
||
// 使用fetch预热缓存,但不等待响应
|
||
fetch(url, {
|
||
method: 'HEAD',
|
||
mode: 'no-cors'
|
||
}).catch(() => {
|
||
// 忽略错误,这只是预热缓存
|
||
});
|
||
});
|
||
}
|
||
|
||
// 延迟加载非关键资源
|
||
static deferNonCriticalResources() {
|
||
if (typeof window === 'undefined') return;
|
||
|
||
// 延迟加载第三方脚本
|
||
setTimeout(() => {
|
||
// 可以在这里加载分析脚本、客服组件等
|
||
interface ScriptConfig {
|
||
src: string;
|
||
async?: boolean;
|
||
}
|
||
|
||
const scripts: ScriptConfig[] = [
|
||
// 示例:{ src: 'https://example.com/analytics.js', async: true },
|
||
];
|
||
|
||
scripts.forEach(script => {
|
||
// 检查脚本是否已经存在
|
||
if (!document.querySelector(`script[src="${script.src}"]`)) {
|
||
const scriptElement = document.createElement('script');
|
||
scriptElement.src = script.src;
|
||
scriptElement.async = script.async || true;
|
||
document.head.appendChild(scriptElement);
|
||
}
|
||
});
|
||
}, 3000); // 3秒后加载
|
||
}
|
||
}
|
||
|
||
// 页面加载优化Hook
|
||
let optimizationInitialized = false;
|
||
let imageLoadingCleanup: (() => void) | null = null;
|
||
|
||
export function usePageLoadOptimization() {
|
||
if (typeof window !== 'undefined' && !optimizationInitialized) {
|
||
optimizationInitialized = true;
|
||
|
||
// 预加载关键资源
|
||
setTimeout(() => {
|
||
PerformanceOptimizer.preloadCriticalResources();
|
||
}, performanceConfig.prefetch.prefetchDelay);
|
||
|
||
// 监控性能
|
||
PerformanceOptimizer.monitorPerformance();
|
||
|
||
// 优化图片加载
|
||
imageLoadingCleanup = PerformanceOptimizer.optimizeImageLoading() || null;
|
||
|
||
// 延迟加载非关键资源
|
||
PerformanceOptimizer.deferNonCriticalResources();
|
||
|
||
// 页面卸载和路由变化时清理
|
||
const cleanup = () => {
|
||
optimizationInitialized = false;
|
||
if (imageLoadingCleanup) {
|
||
imageLoadingCleanup();
|
||
imageLoadingCleanup = null;
|
||
}
|
||
|
||
// 清理预加载的脚本和链接
|
||
try {
|
||
const preloadedScripts = document.querySelectorAll('script[data-perf-preload]');
|
||
const preloadedLinks = document.querySelectorAll('link[data-perf-preload]');
|
||
|
||
preloadedScripts.forEach(script => {
|
||
if (script.parentNode) {
|
||
script.parentNode.removeChild(script);
|
||
}
|
||
});
|
||
|
||
preloadedLinks.forEach(link => {
|
||
if (link.parentNode) {
|
||
link.parentNode.removeChild(link);
|
||
}
|
||
});
|
||
} catch (error) {
|
||
console.warn('清理预加载资源时出错:', error);
|
||
}
|
||
};
|
||
|
||
// 监听各种页面卸载和路由变化事件
|
||
window.addEventListener('beforeunload', cleanup);
|
||
|
||
// 监听 Next.js 路由变化 (用于 SPA 导航)
|
||
if (typeof window !== 'undefined' && window.history) {
|
||
const originalPushState = window.history.pushState;
|
||
const originalReplaceState = window.history.replaceState;
|
||
|
||
window.history.pushState = function(...args) {
|
||
cleanup();
|
||
return originalPushState.apply(this, args);
|
||
};
|
||
|
||
window.history.replaceState = function(...args) {
|
||
cleanup();
|
||
return originalReplaceState.apply(this, args);
|
||
};
|
||
|
||
window.addEventListener('popstate', cleanup);
|
||
}
|
||
|
||
return cleanup;
|
||
}
|
||
}
|