diff --git a/src/hooks/useFirstScreenMetrics.ts b/src/hooks/useFirstScreenMetrics.ts index 58b1afc6..10dda43b 100644 --- a/src/hooks/useFirstScreenMetrics.ts +++ b/src/hooks/useFirstScreenMetrics.ts @@ -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(null); - // 使用 ref 记录页面加载开始时间 - const pageLoadStartRef = useRef(performance.now()); - const skeletonStartRef = useRef(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(performance.now()); - const skeletonEndRef = useRef(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,