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