refactor: 使用 performanceMonitor 替换 useFirstScreenMetrics 中的 performance.now()

- useFirstScreenMetrics: 用 performanceMonitor.mark/measure 替换手动时间计算
- useSkeletonTiming: 用 usePerformanceMark Hook 重构,支持自定义前缀
- 所有性能数据统一到 performanceMonitor,可通过 generateReport() 查看

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
zdl
2025-12-08 19:33:26 +08:00
parent 65f71603e1
commit a8c8fe4211

View File

@@ -16,6 +16,8 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import { initWebVitalsTracking, getCachedMetrics } from '@utils/performance/webVitals';
import { collectResourceStats, collectApiStats } from '@utils/performance/resourceMonitor';
import { performanceMonitor } from '@utils/performanceMonitor';
import { usePerformanceMark } from '@hooks/usePerformanceTracker';
import posthog from 'posthog-js';
import type {
FirstScreenMetrics,
@@ -44,11 +46,17 @@ export const useFirstScreenMetrics = (
const [isLoading, setIsLoading] = useState(true);
const [metrics, setMetrics] = useState<FirstScreenMetrics | null>(null);
// 使用 ref 记录页面加载开始时间
const pageLoadStartRef = useRef<number>(performance.now());
const skeletonStartRef = useRef<number>(performance.now());
// 使用 ref 避免重复标
const hasMarkedRef = useRef(false);
const hasInitializedRef = useRef(false);
// 在组件首次渲染时标记开始时间点
if (!hasMarkedRef.current) {
hasMarkedRef.current = true;
performanceMonitor.mark(`${pageType}-page-load-start`);
performanceMonitor.mark(`${pageType}-skeleton-start`);
}
/**
* 收集所有首屏指标
*/
@@ -82,12 +90,20 @@ export const useFirstScreenMetrics = (
customProperties,
});
// 5. 计算首屏可交互时间TTI
const now = performance.now();
const timeToInteractive = now - pageLoadStartRef.current;
// 5. 标记可交互时间点,并计算 TTI
performanceMonitor.mark(`${pageType}-interactive`);
const timeToInteractive = performanceMonitor.measure(
`${pageType}-page-load-start`,
`${pageType}-interactive`,
`${pageType} TTI`
) || 0;
// 6. 计算骨架屏展示时长
const skeletonDisplayDuration = now - skeletonStartRef.current;
const skeletonDisplayDuration = performanceMonitor.measure(
`${pageType}-skeleton-start`,
`${pageType}-interactive`,
`${pageType} 骨架屏时长`
) || 0;
const firstScreenMetrics: FirstScreenMetrics = {
webVitals,
@@ -143,9 +159,9 @@ export const useFirstScreenMetrics = (
const remeasure = useCallback(() => {
setIsLoading(true);
// 重置计时器
pageLoadStartRef.current = performance.now();
skeletonStartRef.current = performance.now();
// 重置性能标记
performanceMonitor.mark(`${pageType}-page-load-start`);
performanceMonitor.mark(`${pageType}-skeleton-start`);
// 延迟收集指标(等待 Web Vitals 完成)
setTimeout(() => {
@@ -247,7 +263,7 @@ export const useFirstScreenMetrics = (
*
* 使用示例:
* ```tsx
* const { markSkeletonEnd } = useSkeletonTiming();
* const { markSkeletonEnd } = useSkeletonTiming('home-skeleton');
*
* useEffect(() => {
* if (!loading) {
@@ -256,27 +272,32 @@ export const useFirstScreenMetrics = (
* }, [loading, markSkeletonEnd]);
* ```
*/
export const useSkeletonTiming = () => {
const skeletonStartRef = useRef<number>(performance.now());
const skeletonEndRef = useRef<number | null>(null);
export const useSkeletonTiming = (prefix = 'skeleton') => {
const { mark, getMeasure } = usePerformanceMark(prefix);
const hasMarkedEndRef = useRef(false);
const hasMarkedStartRef = useRef(false);
// 在组件首次渲染时标记开始
if (!hasMarkedStartRef.current) {
hasMarkedStartRef.current = true;
mark('start');
}
const markSkeletonEnd = useCallback(() => {
if (!skeletonEndRef.current) {
skeletonEndRef.current = performance.now();
const duration = skeletonEndRef.current - skeletonStartRef.current;
if (!hasMarkedEndRef.current) {
hasMarkedEndRef.current = true;
mark('end');
const duration = getMeasure('start', 'end');
if (process.env.NODE_ENV === 'development') {
if (process.env.NODE_ENV === 'development' && duration) {
console.log(`⏱️ Skeleton Display Duration: ${(duration / 1000).toFixed(2)}s`);
}
}
}, []);
}, [mark, getMeasure]);
const getSkeletonDuration = useCallback((): number | null => {
if (skeletonEndRef.current) {
return skeletonEndRef.current - skeletonStartRef.current;
}
return null;
}, []);
return getMeasure('start', 'end');
}, [getMeasure]);
return {
markSkeletonEnd,