// 前端性能優化工具模組 /** * 防抖函數 - 防止過度頻繁的 API 調用 */ export function debounce any>( func: T, delay: number ): (...args: Parameters) => void { let timeoutId: NodeJS.Timeout; return (...args: Parameters) => { clearTimeout(timeoutId); timeoutId = setTimeout(() => func.apply(null, args), delay); }; } /** * 節流函數 - 限制函數執行頻率 */ export function throttle any>( func: T, limit: number ): (...args: Parameters) => void { let inThrottle: boolean; return (...args: Parameters) => { if (!inThrottle) { func.apply(null, args); inThrottle = true; setTimeout(() => inThrottle = false, limit); } }; } /** * 記憶化函數 - 快取函數執行結果 */ export function memoize any>(func: T): T { const cache = new Map(); return ((...args: any[]) => { const key = JSON.stringify(args); if (cache.has(key)) { return cache.get(key); } const result = func.apply(null, args); cache.set(key, result); return result; }) as T; } /** * 簡單的本地快取實作 */ export class LocalCache { private static instance: LocalCache; private cache = new Map(); public static getInstance(): LocalCache { if (!LocalCache.instance) { LocalCache.instance = new LocalCache(); } return LocalCache.instance; } set(key: string, value: any, ttlMs: number = 300000): void { // 預設5分鐘 const expiry = Date.now() + ttlMs; this.cache.set(key, { data: value, expiry }); } get(key: string): T | null { const item = this.cache.get(key); if (!item) { return null; } if (Date.now() > item.expiry) { this.cache.delete(key); return null; } return item.data as T; } has(key: string): boolean { const item = this.cache.get(key); if (!item) return false; if (Date.now() > item.expiry) { this.cache.delete(key); return false; } return true; } clear(): void { this.cache.clear(); } // 清理過期項目 cleanup(): void { const now = Date.now(); for (const [key, item] of this.cache) { if (now > item.expiry) { this.cache.delete(key); } } } } /** * API 請求快取包裝器 */ export async function cachedApiCall( key: string, apiCall: () => Promise, ttlMs: number = 300000 ): Promise { const cache = LocalCache.getInstance(); // 檢查快取 const cached = cache.get(key); if (cached) { console.log(`Cache hit for key: ${key}`); return cached; } // 執行 API 調用 console.log(`Cache miss for key: ${key}, making API call`); const result = await apiCall(); // 存入快取 cache.set(key, result, ttlMs); return result; } /** * 生成快取鍵 */ export function generateCacheKey(prefix: string, ...params: any[]): string { const paramString = params.map(p => typeof p === 'object' ? JSON.stringify(p) : String(p) ).join('_'); return `${prefix}_${paramString}`; } /** * 性能監控工具 */ export class PerformanceMonitor { private static timers = new Map(); static start(label: string): void { this.timers.set(label, performance.now()); } static end(label: string): number { const startTime = this.timers.get(label); if (!startTime) { console.warn(`No timer found for label: ${label}`); return 0; } const duration = performance.now() - startTime; this.timers.delete(label); console.log(`⏱️ ${label}: ${duration.toFixed(2)}ms`); return duration; } static measure(label: string, fn: () => T): T { this.start(label); const result = fn(); this.end(label); return result; } static async measureAsync(label: string, fn: () => Promise): Promise { this.start(label); const result = await fn(); this.end(label); return result; } } /** * 圖片懶加載 Hook 替代方案 */ export function createIntersectionObserver( callback: (entry: IntersectionObserverEntry) => void, options?: IntersectionObserverInit ): IntersectionObserver { const defaultOptions: IntersectionObserverInit = { root: null, rootMargin: '50px', threshold: 0.1, ...options }; return new IntersectionObserver((entries) => { entries.forEach(callback); }, defaultOptions); }