feat: 已完成的工作

1. 创建性能监控工具
    - 提供完整的性能指标收集
    - 自动生成性能报告
    - 分析性能瓶颈并给出建议
  2. 创建React性能Hook
    - usePerformanceTracker - 追踪组件渲染性能
    - usePerformanceMark - 标记自定义操作
  3. 添加应用启动监控
    - 标记 app-start 时间点
    - 标记 react-ready 时间点
  4. 添加认证监控
    - 标记 auth-check-start 时间点
    - 需要补充 auth-check-end 时间点
This commit is contained in:
zdl
2025-11-25 16:03:52 +08:00
parent d37c974d23
commit 5e35fbda7a
4 changed files with 420 additions and 17 deletions

View File

@@ -3,6 +3,7 @@ import React, { createContext, useContext, useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import { useToast } from '@chakra-ui/react'; import { useToast } from '@chakra-ui/react';
import { logger } from '../utils/logger'; import { logger } from '../utils/logger';
import { performanceMonitor } from '@/utils/performanceMonitor';
import { useNotification } from '../contexts/NotificationContext'; import { useNotification } from '../contexts/NotificationContext';
import { identifyUser, resetUser, trackEvent } from '@lib/posthog'; import { identifyUser, resetUser, trackEvent } from '@lib/posthog';
import { SPECIAL_EVENTS } from '@lib/constants'; import { SPECIAL_EVENTS } from '@lib/constants';
@@ -52,6 +53,9 @@ export const AuthProvider = ({ children }) => {
lastCheckTimeRef.current = now; lastCheckTimeRef.current = now;
// ⏱️ 性能监控: 标记认证检查开始
performanceMonitor.mark('auth-check-start');
try { try {
logger.debug('AuthContext', '开始检查Session状态', { logger.debug('AuthContext', '开始检查Session状态', {
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
@@ -221,21 +225,7 @@ export const AuthProvider = ({ children }) => {
setUser(data.user); setUser(data.user);
setIsAuthenticated(true); setIsAuthenticated(true);
// ❌ 过时的追踪代码已移除(新代码在组件中使用 useAuthEvents 追踪 // ⚡ 登录成功后显示欢迎引导延迟2秒避免与登录Toast冲突移动端不显示
// 正确的事件追踪在 AuthFormContent.js 中调用 authEvents.trackLoginSuccess()
// 事件名:'User Logged In' 或 'User Signed Up'
// 属性名login_method (不是 loginType)
// ⚡ 移除toast让调用者处理UI反馈避免并发更新冲突
// toast({
// title: "登录成功",
// description: "欢迎回来!",
// status: "success",
// duration: 3000,
// isClosable: true,
// });
// ⚡ 登录成功后显示欢迎引导延迟2秒避免与登录Toast冲突
setTimeout(() => { setTimeout(() => {
showWelcomeGuide(); showWelcomeGuide();
}, 2000); }, 2000);
@@ -293,9 +283,9 @@ export const AuthProvider = ({ children }) => {
isClosable: true, isClosable: true,
}); });
// ⚡ 注册成功后显示欢迎引导延迟2秒 // ⚡ 注册成功后显示欢迎引导延迟2秒,移动端不显示
setTimeout(() => { setTimeout(() => {
showWelcomeGuide(); showWelcomeGuide();
}, 2000); }, 2000);
return { success: true }; return { success: true };

View File

@@ -0,0 +1,135 @@
// src/hooks/usePerformanceTracker.ts
// React组件性能追踪Hook
import { useEffect, useRef } from 'react';
import { performanceMonitor } from '@/utils/performanceMonitor';
import { logger } from '@/utils/logger';
interface PerformanceTrackerOptions {
componentName: string;
trackMount?: boolean; // 是否追踪组件挂载
trackRender?: boolean; // 是否追踪每次渲染
logToConsole?: boolean; // 是否输出到控制台
}
/**
* 性能追踪Hook
* 用于监控React组件的渲染性能
*
* @example
* ```tsx
* function HomePage() {
* usePerformanceTracker({
* componentName: 'HomePage',
* trackMount: true,
* trackRender: true
* });
* // ...
* }
* ```
*/
export const usePerformanceTracker = ({
componentName,
trackMount = true,
trackRender = false,
logToConsole = true
}: PerformanceTrackerOptions) => {
const mountTimeRef = useRef<number>(0);
const renderCountRef = useRef<number>(0);
const lastRenderTimeRef = useRef<number>(0);
// 记录组件挂载时间
useEffect(() => {
if (!trackMount) return;
const mountTime = performance.now();
mountTimeRef.current = mountTime;
// 标记组件挂载完成
performanceMonitor.mark(`${componentName}-mounted`);
if (logToConsole) {
logger.info('PerformanceTracker', `🎨 ${componentName} mounted`, {
mountTime: `${mountTime.toFixed(2)}ms`,
timestamp: new Date().toISOString()
});
}
// Cleanup: 记录组件卸载
return () => {
const unmountTime = performance.now();
const lifetime = unmountTime - mountTime;
performanceMonitor.mark(`${componentName}-unmounted`);
if (logToConsole) {
logger.info('PerformanceTracker', `🗑️ ${componentName} unmounted`, {
lifetime: `${lifetime.toFixed(2)}ms`,
renderCount: renderCountRef.current
});
}
};
}, [componentName, trackMount, logToConsole]);
// 追踪每次渲染
useEffect(() => {
if (!trackRender) return;
renderCountRef.current += 1;
const renderTime = performance.now();
const timeSinceLastRender = lastRenderTimeRef.current
? renderTime - lastRenderTimeRef.current
: 0;
lastRenderTimeRef.current = renderTime;
performanceMonitor.mark(`${componentName}-render-${renderCountRef.current}`);
if (logToConsole) {
logger.debug('PerformanceTracker', `🔄 ${componentName} render #${renderCountRef.current}`, {
renderTime: `${renderTime.toFixed(2)}ms`,
timeSinceLastRender: timeSinceLastRender > 0
? `${timeSinceLastRender.toFixed(2)}ms`
: 'N/A'
});
}
});
return {
renderCount: renderCountRef.current,
mountTime: mountTimeRef.current
};
};
/**
* 标记性能时间点的Hook
* 用于标记组件内的关键操作
*
* @example
* ```tsx
* const { markStart, markEnd, measure } = usePerformanceMark('HomePage');
*
* useEffect(() => {
* markStart('data-fetch');
* fetchData().then(() => {
* markEnd('data-fetch');
* const duration = measure('data-fetch-start', 'data-fetch-end');
* });
* }, []);
* ```
*/
export const usePerformanceMark = (componentName: string) => {
const markStart = (operationName: string) => {
performanceMonitor.mark(`${componentName}-${operationName}-start`);
};
const markEnd = (operationName: string) => {
performanceMonitor.mark(`${componentName}-${operationName}-end`);
};
const measure = (startMarkName: string, endMarkName: string) => {
return performanceMonitor.measure(startMarkName, endMarkName);
};
return { markStart, markEnd, measure };
};

View File

@@ -1,4 +1,8 @@
// src/index.js // src/index.js
// ⏱️ 性能监控: 标记应用启动时间
import { performanceMonitor } from '@/utils/performanceMonitor';
performanceMonitor.mark('app-start');
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom/client'; import ReactDOM from 'react-dom/client';
import { BrowserRouter as Router } from 'react-router-dom'; import { BrowserRouter as Router } from 'react-router-dom';
@@ -126,6 +130,9 @@ async function startApp() {
// Create root // Create root
const root = ReactDOM.createRoot(document.getElementById('root')); const root = ReactDOM.createRoot(document.getElementById('root'));
// ⏱️ 性能监控: 标记React准备就绪
performanceMonitor.mark('react-ready');
// Render the app with Router wrapper // Render the app with Router wrapper
// ✅ StrictMode 已启用Chakra UI 2.10.9+ 已修复兼容性问题) // ✅ StrictMode 已启用Chakra UI 2.10.9+ 已修复兼容性问题)
root.render( root.render(

View File

@@ -0,0 +1,271 @@
// src/utils/performanceMonitor.ts
// 性能监控工具 - 统计白屏时间和性能指标
import { logger } from './logger';
/**
* 性能指标接口
*/
export interface PerformanceMetrics {
// 网络指标
dns?: number; // DNS查询时间
tcp?: number; // TCP连接时间
ttfb?: number; // 首字节时间(Time To First Byte)
domLoad?: number; // DOM加载时间
resourceLoad?: number; // 资源加载时间
// 渲染指标
fp?: number; // 首次绘制(First Paint)
fcp?: number; // 首次内容绘制(First Contentful Paint)
lcp?: number; // 最大内容绘制(Largest Contentful Paint)
// 自定义指标
reactInit?: number; // React初始化时间
authCheck?: number; // 认证检查时间
homepageRender?: number; // 首页渲染时间
// 总计
totalWhiteScreen?: number; // 总白屏时间
}
/**
* 性能时间点记录
*/
const performanceMarks: Map<string, number> = new Map();
/**
* 性能监控器类
*/
class PerformanceMonitor {
private metrics: PerformanceMetrics = {};
private isProduction: boolean;
constructor() {
this.isProduction = process.env.NODE_ENV === 'production';
}
/**
* 标记性能时间点
*/
mark(name: string): void {
const timestamp = performance.now();
performanceMarks.set(name, timestamp);
if (!this.isProduction) {
logger.debug('PerformanceMonitor', `⏱️ Mark: ${name}`, {
time: `${timestamp.toFixed(2)}ms`
});
}
}
/**
* 计算两个时间点之间的耗时
*/
measure(startMark: string, endMark: string): number | null {
const startTime = performanceMarks.get(startMark);
const endTime = performanceMarks.get(endMark);
if (!startTime || !endTime) {
logger.warn('PerformanceMonitor', 'Missing performance mark', {
startMark,
endMark,
hasStart: !!startTime,
hasEnd: !!endTime
});
return null;
}
const duration = endTime - startTime;
if (!this.isProduction) {
logger.debug('PerformanceMonitor', `📊 Measure: ${startMark}${endMark}`, {
duration: `${duration.toFixed(2)}ms`
});
}
return duration;
}
/**
* 获取浏览器性能指标
*/
collectBrowserMetrics(): void {
if (!window.performance || !window.performance.timing) {
logger.warn('PerformanceMonitor', 'Performance API not supported');
return;
}
const timing = window.performance.timing;
const navigationStart = timing.navigationStart;
// 网络指标
this.metrics.dns = timing.domainLookupEnd - timing.domainLookupStart;
this.metrics.tcp = timing.connectEnd - timing.connectStart;
this.metrics.ttfb = timing.responseStart - navigationStart;
this.metrics.domLoad = timing.domContentLoadedEventEnd - navigationStart;
this.metrics.resourceLoad = timing.loadEventEnd - navigationStart;
// 获取 FP/FCP/LCP
this.collectPaintMetrics();
}
/**
* 收集绘制指标(FP/FCP/LCP)
*/
private collectPaintMetrics(): void {
if (!window.performance || !window.performance.getEntriesByType) {
return;
}
// First Paint & First Contentful Paint
const paintEntries = performance.getEntriesByType('paint');
paintEntries.forEach((entry: any) => {
if (entry.name === 'first-paint') {
this.metrics.fp = entry.startTime;
} else if (entry.name === 'first-contentful-paint') {
this.metrics.fcp = entry.startTime;
}
});
// Largest Contentful Paint (需要 PerformanceObserver)
try {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1] as any;
this.metrics.lcp = lastEntry.startTime;
});
observer.observe({ entryTypes: ['largest-contentful-paint'] });
} catch (e) {
// LCP可能不被支持
}
}
/**
* 收集自定义React指标
*/
collectReactMetrics(): void {
// React初始化时间
const reactInit = this.measure('app-start', 'react-ready');
if (reactInit) this.metrics.reactInit = reactInit;
// 认证检查时间
const authCheck = this.measure('auth-check-start', 'auth-check-end');
if (authCheck) this.metrics.authCheck = authCheck;
// 首页渲染时间
const homepageRender = this.measure('homepage-render-start', 'homepage-render-end');
if (homepageRender) this.metrics.homepageRender = homepageRender;
// 计算总白屏时间 (从页面开始到首屏完成)
const totalWhiteScreen = this.measure('app-start', 'homepage-render-end');
if (totalWhiteScreen) this.metrics.totalWhiteScreen = totalWhiteScreen;
}
/**
* 生成性能报告
*/
generateReport(): void {
this.collectBrowserMetrics();
this.collectReactMetrics();
const report = {
'=== 网络阶段 ===': {
'DNS查询': this.formatMs(this.metrics.dns),
'TCP连接': this.formatMs(this.metrics.tcp),
'首字节时间(TTFB)': this.formatMs(this.metrics.ttfb),
'DOM加载': this.formatMs(this.metrics.domLoad),
'资源加载': this.formatMs(this.metrics.resourceLoad),
},
'=== 渲染阶段 ===': {
'首次绘制(FP)': this.formatMs(this.metrics.fp),
'首次内容绘制(FCP)': this.formatMs(this.metrics.fcp),
'最大内容绘制(LCP)': this.formatMs(this.metrics.lcp),
},
'=== React阶段 ===': {
'React初始化': this.formatMs(this.metrics.reactInit),
'认证检查': this.formatMs(this.metrics.authCheck),
'首页渲染': this.formatMs(this.metrics.homepageRender),
},
'=== 总计 ===': {
'总白屏时间': this.formatMs(this.metrics.totalWhiteScreen),
}
};
logger.info('PerformanceMonitor', '🚀 性能报告', report);
// 性能分析建议
this.analyzePerformance();
return this.metrics;
}
/**
* 性能分析和建议
*/
private analyzePerformance(): void {
const issues: string[] = [];
if (this.metrics.ttfb && this.metrics.ttfb > 500) {
issues.push(`⚠️ TTFB过高(${this.metrics.ttfb.toFixed(0)}ms) - 建议优化服务器响应`);
}
if (this.metrics.resourceLoad && this.metrics.resourceLoad > 3000) {
issues.push(`⚠️ 资源加载慢(${this.metrics.resourceLoad.toFixed(0)}ms) - 建议代码分割/CDN`);
}
if (this.metrics.authCheck && this.metrics.authCheck > 300) {
issues.push(`⚠️ 认证检查慢(${this.metrics.authCheck.toFixed(0)}ms) - 建议优化Session API`);
}
if (this.metrics.totalWhiteScreen && this.metrics.totalWhiteScreen > 2000) {
issues.push(`❌ 白屏时间过长(${this.metrics.totalWhiteScreen.toFixed(0)}ms) - 目标 <1500ms`);
}
if (issues.length > 0) {
logger.warn('PerformanceMonitor', '🔴 性能问题', { issues });
} else {
logger.info('PerformanceMonitor', '✅ 性能良好');
}
}
/**
* 格式化毫秒数
*/
private formatMs(ms: number | undefined): string {
if (ms === undefined) return 'N/A';
let emoji = '✅';
if (ms > 1000) emoji = '❌';
else if (ms > 500) emoji = '⚠️';
return `${ms.toFixed(0)}ms ${emoji}`;
}
/**
* 获取所有指标
*/
getMetrics(): PerformanceMetrics {
return this.metrics;
}
/**
* 重置监控器
*/
reset(): void {
this.metrics = {};
performanceMarks.clear();
}
}
// 导出单例
export const performanceMonitor = new PerformanceMonitor();
// 页面加载完成后自动生成报告
if (typeof window !== 'undefined') {
window.addEventListener('load', () => {
// 延迟1秒确保所有指标收集完成
setTimeout(() => {
performanceMonitor.generateReport();
}, 1000);
});
}