From cb46971e0ecfe0c5c2de97ae3ce8748cdf90a28b Mon Sep 17 00:00:00 2001
From: zdl <3489966805@qq.com>
Date: Tue, 25 Nov 2025 16:36:18 +0800
Subject: [PATCH] =?UTF-8?q?feat:1=EF=B8=8F=E2=83=A3=20=E5=A2=9E=E5=BC=BA?=
=?UTF-8?q?=20performanceMonitor.ts?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- ✅ 新增 measure(name, startMark, endMark) 方法(支持命名测量)
- ✅ 新增 getMarks() - 获取所有性能标记
- ✅ 新增 getMeasures() - 获取所有测量结果
- ✅ 新增 getReport() - 返回完整 JSON 报告
- ✅ 新增 exportJSON() - 导出 JSON 文件
- ✅ 新增 reportToPostHog() - 上报到 PostHog
- ✅ 新增全局 API window.__PERFORMANCE__(仅开发环境)
- ✅ 彩色控制台使用说明
2️⃣ 添加 PostHog 性能上报
- ✅ 在 posthog.js 中新增 reportPerformanceMetrics() 函数
- ✅ 上报所有关键性能指标(网络、渲染、React)
- ✅ 自动计算性能评分(0-100)
- ✅ 包含浏览器和设备信息
---
.env.development | 5 +
.env.production | 8 +
src/App.js | 2 +
src/components/PerformancePanel.tsx | 384 ++++++++++++++++++++++++++++
src/lib/posthog.js | 101 ++++++++
5 files changed, 500 insertions(+)
create mode 100644 src/components/PerformancePanel.tsx
diff --git a/.env.development b/.env.development
index d17b1104..d16e7aa7 100644
--- a/.env.development
+++ b/.env.development
@@ -18,3 +18,8 @@ REACT_APP_ENABLE_MOCK=false
# 开发环境标识
REACT_APP_ENV=development
+
+# 性能监控配置
+REACT_APP_ENABLE_PERFORMANCE_MONITOR=true
+REACT_APP_ENABLE_PERFORMANCE_PANEL=true
+REACT_APP_REPORT_TO_POSTHOG=false
diff --git a/.env.production b/.env.production
index 3eb056e9..2d9689ac 100644
--- a/.env.production
+++ b/.env.production
@@ -37,3 +37,11 @@ TSC_COMPILE_ON_ERROR=true
IMAGE_INLINE_SIZE_LIMIT=10000
# Node.js 内存限制(适用于大型项目)
NODE_OPTIONS=--max_old_space_size=4096
+
+# 性能监控配置(生产环境)
+# 启用性能监控
+REACT_APP_ENABLE_PERFORMANCE_MONITOR=true
+# 禁用性能面板(仅开发环境)
+REACT_APP_ENABLE_PERFORMANCE_PANEL=false
+# 启用 PostHog 性能数据上报
+REACT_APP_REPORT_TO_POSTHOG=true
diff --git a/src/App.js b/src/App.js
index 927e6805..cb045fc7 100755
--- a/src/App.js
+++ b/src/App.js
@@ -21,6 +21,7 @@ import AppProviders from './providers/AppProviders';
// Components
import GlobalComponents from './components/GlobalComponents';
+import { PerformancePanel } from './components/PerformancePanel';
// Hooks
import { useGlobalErrorHandler } from './hooks/useGlobalErrorHandler';
@@ -132,6 +133,7 @@ export default function App() {
+
);
}
\ No newline at end of file
diff --git a/src/components/PerformancePanel.tsx b/src/components/PerformancePanel.tsx
new file mode 100644
index 00000000..c286c9af
--- /dev/null
+++ b/src/components/PerformancePanel.tsx
@@ -0,0 +1,384 @@
+// src/components/PerformancePanel.tsx
+// 性能监控可视化面板 - 仅开发环境显示
+
+import React, { useState, useEffect } from 'react';
+import {
+ Box,
+ VStack,
+ HStack,
+ Text,
+ Badge,
+ Button,
+ IconButton,
+ Stat,
+ StatLabel,
+ StatNumber,
+ StatHelpText,
+ Accordion,
+ AccordionItem,
+ AccordionButton,
+ AccordionPanel,
+ AccordionIcon,
+ useDisclosure,
+ Drawer,
+ DrawerBody,
+ DrawerHeader,
+ DrawerOverlay,
+ DrawerContent,
+ DrawerCloseButton,
+} from '@chakra-ui/react';
+import { MdSpeed, MdClose, MdRefresh, MdFileDownload } from 'react-icons/md';
+import { performanceMonitor } from '@/utils/performanceMonitor';
+
+/**
+ * 性能评分颜色映射
+ */
+const getScoreColor = (score: string): string => {
+ switch (score) {
+ case 'excellent':
+ return 'green';
+ case 'good':
+ return 'blue';
+ case 'needs improvement':
+ return 'yellow';
+ case 'poor':
+ return 'red';
+ default:
+ return 'gray';
+ }
+};
+
+/**
+ * 格式化毫秒数
+ */
+const formatMs = (ms: number | undefined): string => {
+ if (ms === undefined) return 'N/A';
+ return `${ms.toFixed(0)}ms`;
+};
+
+/**
+ * 性能面板组件
+ */
+export const PerformancePanel: React.FC = () => {
+ const { isOpen, onOpen, onClose } = useDisclosure();
+ const [report, setReport] = useState(null);
+
+ // 刷新性能数据
+ const refreshData = () => {
+ const newReport = performanceMonitor.getReport();
+ setReport(newReport);
+ };
+
+ // 导出 JSON
+ const exportJSON = () => {
+ const json = performanceMonitor.exportJSON();
+ const blob = new Blob([json], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement('a');
+ a.href = url;
+ a.download = `performance-report-${Date.now()}.json`;
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ URL.revokeObjectURL(url);
+ };
+
+ // 初始加载
+ useEffect(() => {
+ refreshData();
+ }, []);
+
+ // 仅在开发环境显示
+ if (process.env.NODE_ENV !== 'development') {
+ return null;
+ }
+
+ return (
+ <>
+ {/* 浮动按钮 */}
+ }
+ position="fixed"
+ bottom="20px"
+ right="20px"
+ colorScheme="blue"
+ size="lg"
+ borderRadius="full"
+ boxShadow="lg"
+ zIndex={9999}
+ onClick={onOpen}
+ />
+
+ {/* 抽屉面板 */}
+
+
+
+
+
+
+
+ 性能监控面板
+
+
+
+
+ {report ? (
+
+ {/* 操作按钮 */}
+
+ }
+ size="sm"
+ colorScheme="blue"
+ onClick={refreshData}
+ >
+ 刷新数据
+
+ }
+ size="sm"
+ colorScheme="green"
+ onClick={exportJSON}
+ >
+ 导出 JSON
+
+
+
+ {/* 总览 */}
+
+
+
+ 性能评分
+
+ {report.summary.performanceScore.toUpperCase()}
+
+
+
+
+ 性能标记: {report.summary.totalMarks}
+
+
+ 性能测量: {report.summary.totalMeasures}
+
+
+
+
+
+ {/* 网络指标 */}
+
+
+ 网络指标
+
+
+
+
+
+
+
+
+ {/* 渲染指标 */}
+
+
+ 渲染指标
+
+
+
+
+
+
+
+
+ {/* React 指标 */}
+
+
+ React 指标
+
+
+
+
+
+
+
+
+ {/* 总白屏时间 */}
+
+
+ 总白屏时间
+
+ {formatMs(report.metrics.totalWhiteScreen)}
+
+
+ {report.metrics.totalWhiteScreen && report.metrics.totalWhiteScreen < 1500
+ ? '✅ 优秀'
+ : report.metrics.totalWhiteScreen && report.metrics.totalWhiteScreen < 2000
+ ? '⚠️ 良好'
+ : '❌ 需要优化'}
+
+
+
+
+ {/* 优化建议 */}
+
+
+
+
+
+ 优化建议 ({report.recommendations.length})
+
+
+
+
+
+
+ {report.recommendations.map((rec: string, index: number) => (
+
+ {rec}
+
+ ))}
+
+
+
+
+ {/* 性能标记 */}
+
+
+
+
+ 性能标记 ({report.marks.length})
+
+
+
+
+
+
+ {report.marks.map((mark: any, index: number) => (
+
+ {mark.name}
+ {mark.time.toFixed(2)}ms
+
+ ))}
+
+
+
+
+ {/* 性能测量 */}
+
+
+
+
+ 性能测量 ({report.measures.length})
+
+
+
+
+
+
+ {report.measures.map((measure: any, index: number) => (
+
+
+
+ {measure.name}
+
+ {measure.duration.toFixed(2)}ms
+
+
+ {measure.startMark} → {measure.endMark}
+
+
+ ))}
+
+
+
+
+
+ ) : (
+ 加载中...
+ )}
+
+
+
+ >
+ );
+};
+
+/**
+ * 指标统计组件
+ */
+interface MetricStatProps {
+ label: string;
+ value: string;
+ threshold: number;
+ actualValue?: number;
+}
+
+const MetricStat: React.FC = ({ label, value, threshold, actualValue }) => {
+ const isGood = actualValue !== undefined && actualValue < threshold;
+
+ return (
+
+ {label}
+
+
+ {value}
+
+ {actualValue !== undefined && (
+ {isGood ? '✅' : '⚠️'}
+ )}
+
+
+ );
+};
+
+export default PerformancePanel;
diff --git a/src/lib/posthog.js b/src/lib/posthog.js
index 3176a5cd..45d632a2 100644
--- a/src/lib/posthog.js
+++ b/src/lib/posthog.js
@@ -293,4 +293,105 @@ export const isFeatureEnabled = (flagKey) => {
}
};
+/**
+ * Report performance metrics to PostHog
+ * @param {object} metrics - Performance metrics object
+ */
+export const reportPerformanceMetrics = (metrics) => {
+ // 仅在生产环境上报
+ if (process.env.NODE_ENV !== 'production') {
+ console.log('📊 [开发环境] 性能指标(未上报到 PostHog):', metrics);
+ return;
+ }
+
+ try {
+ // 获取浏览器和设备信息
+ const browserInfo = {
+ userAgent: navigator.userAgent,
+ viewport: `${window.innerWidth}x${window.innerHeight}`,
+ connection: navigator.connection?.effectiveType || 'unknown',
+ deviceMemory: navigator.deviceMemory || 'unknown',
+ hardwareConcurrency: navigator.hardwareConcurrency || 'unknown',
+ };
+
+ // 上报性能指标
+ posthog.capture('Performance Metrics', {
+ // 网络指标
+ dns_ms: metrics.dns,
+ tcp_ms: metrics.tcp,
+ ttfb_ms: metrics.ttfb,
+ dom_load_ms: metrics.domLoad,
+ resource_load_ms: metrics.resourceLoad,
+
+ // 渲染指标
+ fp_ms: metrics.fp,
+ fcp_ms: metrics.fcp,
+ lcp_ms: metrics.lcp,
+
+ // React 指标
+ react_init_ms: metrics.reactInit,
+ auth_check_ms: metrics.authCheck,
+ homepage_render_ms: metrics.homepageRender,
+
+ // 总计
+ total_white_screen_ms: metrics.totalWhiteScreen,
+
+ // 性能评分
+ performance_score: calculatePerformanceScore(metrics),
+
+ // 浏览器和设备信息
+ ...browserInfo,
+
+ // 时间戳
+ timestamp: new Date().toISOString(),
+ });
+
+ console.log('✅ 性能指标已上报到 PostHog');
+ } catch (error) {
+ console.error('❌ PostHog 性能指标上报失败:', error);
+ }
+};
+
+/**
+ * Calculate overall performance score (0-100)
+ * @param {object} metrics - Performance metrics
+ * @returns {number} Score from 0 to 100
+ */
+const calculatePerformanceScore = (metrics) => {
+ let score = 100;
+
+ // 白屏时间评分(权重 40%)
+ if (metrics.totalWhiteScreen) {
+ if (metrics.totalWhiteScreen > 3000) score -= 40;
+ else if (metrics.totalWhiteScreen > 2000) score -= 20;
+ else if (metrics.totalWhiteScreen > 1500) score -= 10;
+ }
+
+ // TTFB 评分(权重 20%)
+ if (metrics.ttfb) {
+ if (metrics.ttfb > 1000) score -= 20;
+ else if (metrics.ttfb > 500) score -= 10;
+ }
+
+ // LCP 评分(权重 20%)
+ if (metrics.lcp) {
+ if (metrics.lcp > 4000) score -= 20;
+ else if (metrics.lcp > 2500) score -= 10;
+ }
+
+ // FCP 评分(权重 10%)
+ if (metrics.fcp) {
+ if (metrics.fcp > 3000) score -= 10;
+ else if (metrics.fcp > 1800) score -= 5;
+ }
+
+ // 认证检查评分(权重 10%)
+ if (metrics.authCheck) {
+ if (metrics.authCheck > 500) score -= 10;
+ else if (metrics.authCheck > 300) score -= 5;
+ }
+
+ return Math.max(0, Math.min(100, score));
+};
+
export default posthog;