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:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user