Files
cunkebao_v3/Touchkebao/src/utils/performance.ts

205 lines
4.4 KiB
TypeScript
Raw Normal View History

/**
*
*
*/
/**
*
*/
export interface PerformanceResult {
name: string;
duration: number; // 毫秒
timestamp: number;
metadata?: Record<string, any>;
}
/**
*
*/
class PerformanceMonitor {
private results: PerformanceResult[] = [];
private maxResults = 1000; // 最多保存1000条记录
/**
*
*/
measure<T>(
name: string,
fn: () => T,
metadata?: Record<string, any>,
): T {
const start = performance.now();
try {
const result = fn();
const end = performance.now();
this.record(name, end - start, metadata);
return result;
} catch (error) {
const end = performance.now();
this.record(name, end - start, { ...metadata, error: String(error) });
throw error;
}
}
/**
*
*/
async measureAsync<T>(
name: string,
fn: () => Promise<T>,
metadata?: Record<string, any>,
): Promise<T> {
const start = performance.now();
try {
const result = await fn();
const end = performance.now();
this.record(name, end - start, metadata);
return result;
} catch (error) {
const end = performance.now();
this.record(name, end - start, { ...metadata, error: String(error) });
throw error;
}
}
/**
*
*/
private record(
name: string,
duration: number,
metadata?: Record<string, any>,
): void {
const result: PerformanceResult = {
name,
duration,
timestamp: Date.now(),
metadata,
};
this.results.push(result);
// 限制结果数量
if (this.results.length > this.maxResults) {
this.results.shift();
}
// 开发环境下输出到控制台
if (import.meta.env.DEV) {
const color = duration > 100 ? "🔴" : duration > 50 ? "🟡" : "🟢";
console.log(
`${color} [Performance] ${name}: ${duration.toFixed(2)}ms`,
metadata || "",
);
}
}
/**
*
*/
getStats(name?: string): {
count: number;
total: number;
average: number;
min: number;
max: number;
results: PerformanceResult[];
} {
const filtered = name
? this.results.filter(r => r.name === name)
: this.results;
if (filtered.length === 0) {
return {
count: 0,
total: 0,
average: 0,
min: 0,
max: 0,
results: [],
};
}
const durations = filtered.map(r => r.duration);
const total = durations.reduce((sum, d) => sum + d, 0);
const average = total / filtered.length;
const min = Math.min(...durations);
const max = Math.max(...durations);
return {
count: filtered.length,
total,
average,
min,
max,
results: filtered,
};
}
/**
*
*/
getAllResults(): PerformanceResult[] {
return [...this.results];
}
/**
*
*/
clear(): void {
this.results = [];
}
/**
*
*/
export(): string {
return JSON.stringify(this.results, null, 2);
}
}
// 创建全局性能监控实例
export const performanceMonitor = new PerformanceMonitor();
/**
*
*/
export function measurePerformance(name?: string) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor,
) {
const originalMethod = descriptor.value;
const methodName = name || `${target.constructor.name}.${propertyKey}`;
if (typeof originalMethod === "function") {
descriptor.value = function (...args: any[]) {
return performanceMonitor.measure(methodName, () =>
originalMethod.apply(this, args),
);
};
}
return descriptor;
};
}
/**
* HookReact组件
* React组件中使用React
*/
export function usePerformanceMeasure(name: string) {
// 注意这个Hook需要在React组件中使用
// 由于可能造成循环依赖建议在组件中直接使用performanceMonitor.measure
const startRef = { current: performance.now() };
// 返回清理函数
return () => {
if (startRef.current !== null) {
const duration = performance.now() - startRef.current;
performanceMonitor.record(name, duration);
}
};
}