209 lines
4.6 KiB
TypeScript
209 lines
4.6 KiB
TypeScript
// 前端性能優化工具模組
|
|
|
|
/**
|
|
* 防抖函數 - 防止過度頻繁的 API 調用
|
|
*/
|
|
export function debounce<T extends (...args: any[]) => any>(
|
|
func: T,
|
|
delay: number
|
|
): (...args: Parameters<T>) => void {
|
|
let timeoutId: NodeJS.Timeout;
|
|
|
|
return (...args: Parameters<T>) => {
|
|
clearTimeout(timeoutId);
|
|
timeoutId = setTimeout(() => func.apply(null, args), delay);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 節流函數 - 限制函數執行頻率
|
|
*/
|
|
export function throttle<T extends (...args: any[]) => any>(
|
|
func: T,
|
|
limit: number
|
|
): (...args: Parameters<T>) => void {
|
|
let inThrottle: boolean;
|
|
|
|
return (...args: Parameters<T>) => {
|
|
if (!inThrottle) {
|
|
func.apply(null, args);
|
|
inThrottle = true;
|
|
setTimeout(() => inThrottle = false, limit);
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 記憶化函數 - 快取函數執行結果
|
|
*/
|
|
export function memoize<T extends (...args: any[]) => 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<string, { data: any; expiry: number }>();
|
|
|
|
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<T>(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<T>(
|
|
key: string,
|
|
apiCall: () => Promise<T>,
|
|
ttlMs: number = 300000
|
|
): Promise<T> {
|
|
const cache = LocalCache.getInstance();
|
|
|
|
// 檢查快取
|
|
const cached = cache.get<T>(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<string, number>();
|
|
|
|
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<T>(label: string, fn: () => T): T {
|
|
this.start(label);
|
|
const result = fn();
|
|
this.end(label);
|
|
return result;
|
|
}
|
|
|
|
static async measureAsync<T>(label: string, fn: () => Promise<T>): Promise<T> {
|
|
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);
|
|
} |