新增功能:
1. 首次访问追踪 (first_visit)
- 记录用户来源(referrer、UTM参数)
- 记录落地页
- 使用 localStorage 永久标记
2. 首次登录追踪 (first_login)
- 区分首次登录和后续登录
- 按用户 ID 独立标记
- 用于计算新用户激活率
3. 登录/登出事件追踪
- 登录成功追踪 (user_logged_in)
- 登出事件追踪 (user_logged_out,必须在 resetUser 之前)
- 注册事件追踪 (user_registered)
4. 页面浏览时长追踪 (page_view_duration)
- 路由切换时自动计算停留时长
- 页面关闭时发送最终时长
- 过滤停留时间 < 1秒的快速跳转
性能优化:
1. 新增 trackEventAsync 函数
- 使用 requestIdleCallback 在浏览器空闲时发送非关键事件
- Safari 等旧浏览器降级到 setTimeout
- 超时保护(最多延迟 2秒)
2. 异步追踪非关键事件
- first_visit - 不阻塞首屏渲染
- page_view_duration - 不阻塞页面切换
3. 关键事件保持同步
- user_registered、user_logged_in、first_login、user_logged_out
- 确保数据准确性和完整性
分析能力提升:
- ✅ 营销渠道 ROI 分析(UTM 参数追踪)
- ✅ 新用户激活率分析(首次登录标记)
- ✅ 用户留存率分析(注册→首次登录→后续登录)
- ✅ 页面热度分析(停留时长统计)
- ✅ 流失用户识别(7天未登录,需后端支持)
137 lines
4.3 KiB
JavaScript
Executable File
137 lines
4.3 KiB
JavaScript
Executable File
/**
|
|
=========================================================
|
|
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';
|
|
|
|
// Hooks
|
|
import { useGlobalErrorHandler } from './hooks/useGlobalErrorHandler';
|
|
|
|
// Redux
|
|
import { initializePostHog } from './store/slices/posthogSlice';
|
|
|
|
// Utils
|
|
import { logger } from './utils/logger';
|
|
|
|
// PostHog 追踪
|
|
import { trackEvent, trackEventAsync } from '@lib/posthog';
|
|
|
|
// Contexts
|
|
import { useAuth } from '@contexts/AuthContext';
|
|
|
|
/**
|
|
* 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 初始化
|
|
useEffect(() => {
|
|
dispatch(initializePostHog());
|
|
logger.info('App', 'PostHog Redux 初始化已触发');
|
|
}, [dispatch]);
|
|
|
|
// ✅ 首次访问追踪
|
|
useEffect(() => {
|
|
const hasVisited = localStorage.getItem('has_visited');
|
|
|
|
if (!hasVisited) {
|
|
const urlParams = new URLSearchParams(location.search);
|
|
|
|
// ⚡ 使用异步追踪,不阻塞页面渲染
|
|
trackEventAsync('first_visit', {
|
|
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()
|
|
});
|
|
|
|
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) {
|
|
// ⚡ 使用异步追踪,不阻塞页面切换
|
|
trackEventAsync('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 <AppRoutes />;
|
|
}
|
|
|
|
/**
|
|
* App - 应用根组件
|
|
* 设置全局错误处理,提供 Provider 和全局组件
|
|
*/
|
|
export default function App() {
|
|
// 全局错误处理
|
|
useGlobalErrorHandler();
|
|
|
|
return (
|
|
<AppProviders>
|
|
<AppContent />
|
|
<GlobalComponents />
|
|
</AppProviders>
|
|
);
|
|
} |