/**
=========================================================
Vision UI PRO React - v1.0.0
=========================================================
Product Page: https://www.creative-tim.com/product/vision-ui-dashboard-pro-react
Copyright 2021 Creative Tim (https://www.creative-tim.com/)
Design and Coded by Simmmple & Creative Tim
=========================================================
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Visionware.
*/
import React, { useEffect, useRef } from "react";
import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom';
// Routes
import AppRoutes from './routes';
// Providers
import AppProviders from './providers/AppProviders';
// Components
import GlobalComponents from './components/GlobalComponents';
import { PerformancePanel } from './components/PerformancePanel';
// Hooks
import { useGlobalErrorHandler } from './hooks/useGlobalErrorHandler';
// Redux
// ⚡ PostHog 延迟加载:移除同步导入,首屏减少 ~180KB
// import { initializePostHog } from './store/slices/posthogSlice';
import { updateScreenSize } from './store/slices/deviceSlice';
import { injectReducer } from './store';
// Utils
import { logger } from './utils/logger';
import { performanceMonitor } from './utils/performanceMonitor';
// ⚡ PostHog 延迟加载:移除同步导入
// import { trackEvent, trackEventAsync } from '@lib/posthog';
// Contexts
import { useAuth } from '@contexts/AuthContext';
// ⚡ PostHog 延迟加载模块(动态导入后缓存)
let posthogModule = null;
let posthogSliceModule = null;
/**
* ⚡ 延迟加载 PostHog 模块
* 返回 { trackEvent, trackEventAsync, initializePostHog, posthogReducer }
*/
const loadPostHogModules = async () => {
if (posthogModule && posthogSliceModule) {
return { posthogModule, posthogSliceModule };
}
try {
const [posthog, posthogSlice] = await Promise.all([
import('@lib/posthog'),
import('./store/slices/posthogSlice'),
]);
posthogModule = posthog;
posthogSliceModule = posthogSlice;
return { posthogModule, posthogSliceModule };
} catch (error) {
logger.error('App', 'PostHog 模块加载失败', error);
return null;
}
};
/**
* ⚡ 异步追踪事件(延迟加载 PostHog 后调用)
* @param {string} eventName - 事件名称
* @param {object} properties - 事件属性
*/
const trackEventLazy = async (eventName, properties = {}) => {
// 等待模块加载完成
if (!posthogModule) {
const modules = await loadPostHogModules();
if (!modules) return;
}
// 使用异步追踪,不阻塞主线程
posthogModule.trackEventAsync(eventName, properties);
};
/**
* AppContent - 应用核心内容
* 负责 PostHog 初始化和渲染路由
*/
function AppContent() {
const dispatch = useDispatch();
const location = useLocation();
const { isAuthenticated } = useAuth();
// ✅ 使用 Ref 存储页面进入时间和路径(避免闭包问题)
const pageEnterTimeRef = useRef(Date.now());
const currentPathRef = useRef(location.pathname);
// 🎯 ⚡ PostHog 空闲时加载 + Redux 初始化(首屏不加载 ~180KB)
useEffect(() => {
const initPostHogRedux = async () => {
try {
const modules = await loadPostHogModules();
if (!modules) return;
const { posthogSliceModule } = modules;
// 动态注入 PostHog reducer
injectReducer('posthog', posthogSliceModule.default);
// 初始化 PostHog
dispatch(posthogSliceModule.initializePostHog());
// ⚡ 刷新注入前缓存的事件(避免丢失)
const pendingEvents = posthogSliceModule.flushPendingEventsBeforeInjection();
if (pendingEvents.length > 0) {
logger.info('App', `刷新 ${pendingEvents.length} 个注入前缓存的事件`);
pendingEvents.forEach(({ eventName, properties }) => {
posthogModule.trackEventAsync(eventName, properties);
});
}
logger.info('App', 'PostHog 模块空闲时加载完成,Redux 初始化已触发');
} catch (error) {
logger.error('App', 'PostHog 加载失败', error);
}
};
// ⚡ 使用 requestIdleCallback 在浏览器空闲时加载,最长等待 3 秒
if ('requestIdleCallback' in window) {
const idleId = requestIdleCallback(initPostHogRedux, { timeout: 3000 });
return () => cancelIdleCallback(idleId);
} else {
// 降级:Safari 等不支持 requestIdleCallback 的浏览器使用 setTimeout
const timer = setTimeout(initPostHogRedux, 1000);
return () => clearTimeout(timer);
}
}, [dispatch]);
// ⚡ 性能监控:标记 React 初始化完成
useEffect(() => {
performanceMonitor.mark('react-ready');
}, []);
// 📱 设备检测:监听窗口尺寸变化
useEffect(() => {
let resizeTimer;
const handleResize = () => {
// 防抖:避免频繁触发
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => {
dispatch(updateScreenSize());
}, 150);
};
window.addEventListener('resize', handleResize);
window.addEventListener('orientationchange', handleResize);
return () => {
clearTimeout(resizeTimer);
window.removeEventListener('resize', handleResize);
window.removeEventListener('orientationchange', handleResize);
};
}, [dispatch]);
// ✅ 首次访问追踪(🔴 关键事件:立即加载模块,确保数据不丢失)
useEffect(() => {
const hasVisited = localStorage.getItem('has_visited');
if (!hasVisited) {
const urlParams = new URLSearchParams(location.search);
const eventData = {
referrer: document.referrer || 'direct',
utm_source: urlParams.get('utm_source'),
utm_medium: urlParams.get('utm_medium'),
utm_campaign: urlParams.get('utm_campaign'),
landing_page: location.pathname,
timestamp: new Date().toISOString()
};
// 🔴 关键事件:立即加载 PostHog 模块并同步追踪(不使用 trackEventLazy)
// 确保首次访问数据不会因用户快速离开而丢失
(async () => {
const modules = await loadPostHogModules();
if (modules) {
// 使用同步追踪(trackEvent),而非异步追踪(trackEventAsync)
modules.posthogModule.trackEvent('first_visit', eventData);
logger.info('App', '首次访问事件已同步追踪', eventData);
}
})();
localStorage.setItem('has_visited', 'true');
}
}, [location.search, location.pathname]);
// ✅ 页面浏览时长追踪
useEffect(() => {
// 计算上一个页面的停留时长
const calculateAndTrackDuration = () => {
const exitTime = Date.now();
const duration = Math.round((exitTime - pageEnterTimeRef.current) / 1000); // 秒
// 只追踪停留时间 > 1 秒的页面(过滤快速跳转)
if (duration > 1) {
// ⚡ 使用延迟加载的异步追踪,不阻塞页面切换
trackEventLazy('page_view_duration', {
path: currentPathRef.current,
duration_seconds: duration,
is_authenticated: isAuthenticated,
timestamp: new Date().toISOString()
});
}
};
// 路由切换时追踪上一个页面的时长
if (currentPathRef.current !== location.pathname) {
calculateAndTrackDuration();
// 更新为新页面
currentPathRef.current = location.pathname;
pageEnterTimeRef.current = Date.now();
}
// 页面关闭/刷新时追踪时长
const handleBeforeUnload = () => {
calculateAndTrackDuration();
};
window.addEventListener('beforeunload', handleBeforeUnload);
return () => {
window.removeEventListener('beforeunload', handleBeforeUnload);
};
}, [location.pathname, isAuthenticated]);
return ;
}
/**
* App - 应用根组件
* 设置全局错误处理,提供 Provider 和全局组件
*/
export default function App() {
// 全局错误处理
useGlobalErrorHandler();
return (
);
}