Compare commits
3 Commits
2fcc341213
...
d8a4c20565
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8a4c20565 | ||
|
|
5f959fb44f | ||
|
|
ee78e00d3b |
22
src/App.js
22
src/App.js
@@ -28,6 +28,7 @@ import { useGlobalErrorHandler } from './hooks/useGlobalErrorHandler';
|
|||||||
|
|
||||||
// Redux
|
// Redux
|
||||||
import { initializePostHog } from './store/slices/posthogSlice';
|
import { initializePostHog } from './store/slices/posthogSlice';
|
||||||
|
import { updateScreenSize } from './store/slices/deviceSlice';
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
import { logger } from './utils/logger';
|
import { logger } from './utils/logger';
|
||||||
@@ -63,6 +64,27 @@ function AppContent() {
|
|||||||
performanceMonitor.mark('react-ready');
|
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(() => {
|
useEffect(() => {
|
||||||
const hasVisited = localStorage.getItem('has_visited');
|
const hasVisited = localStorage.getItem('has_visited');
|
||||||
|
|||||||
@@ -476,7 +476,8 @@ export default function AuthFormContent() {
|
|||||||
return () => {
|
return () => {
|
||||||
isMountedRef.current = false;
|
isMountedRef.current = false;
|
||||||
};
|
};
|
||||||
}, [authEvents]);
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []); // 仅在挂载时执行一次,避免 countdown 倒计时导致重复触发
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
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 { useNotification } from '../contexts/NotificationContext';
|
import { performanceMonitor } from '@utils/performanceMonitor';
|
||||||
|
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';
|
||||||
|
|
||||||
@@ -37,6 +38,9 @@ export const AuthProvider = ({ children }) => {
|
|||||||
|
|
||||||
// 检查Session状态
|
// 检查Session状态
|
||||||
const checkSession = async () => {
|
const checkSession = async () => {
|
||||||
|
// ⚡ 性能标记:认证检查开始
|
||||||
|
performanceMonitor.mark('auth-check-start');
|
||||||
|
|
||||||
// 节流检查
|
// 节流检查
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
const timeSinceLastCheck = now - lastCheckTimeRef.current;
|
const timeSinceLastCheck = now - lastCheckTimeRef.current;
|
||||||
@@ -47,6 +51,8 @@ export const AuthProvider = ({ children }) => {
|
|||||||
minInterval: `${MIN_CHECK_INTERVAL}ms`,
|
minInterval: `${MIN_CHECK_INTERVAL}ms`,
|
||||||
reason: '距离上次请求间隔太短'
|
reason: '距离上次请求间隔太短'
|
||||||
});
|
});
|
||||||
|
// ⚡ 性能标记:认证检查结束(节流情况)
|
||||||
|
performanceMonitor.mark('auth-check-end');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +131,8 @@ export const AuthProvider = ({ children }) => {
|
|||||||
setUser((prev) => prev === null ? prev : null);
|
setUser((prev) => prev === null ? prev : null);
|
||||||
setIsAuthenticated((prev) => prev === false ? prev : false);
|
setIsAuthenticated((prev) => prev === false ? prev : false);
|
||||||
} finally {
|
} finally {
|
||||||
|
// ⚡ 性能标记:认证检查结束
|
||||||
|
performanceMonitor.mark('auth-check-end');
|
||||||
// ⚡ 只在 isLoading 为 true 时才设置为 false,避免不必要的状态更新
|
// ⚡ 只在 isLoading 为 true 时才设置为 false,避免不必要的状态更新
|
||||||
setIsLoading((prev) => prev === false ? prev : false);
|
setIsLoading((prev) => prev === false ? prev : false);
|
||||||
}
|
}
|
||||||
|
|||||||
35
src/hooks/useDevice.js
Normal file
35
src/hooks/useDevice.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/**
|
||||||
|
* useDevice Hook
|
||||||
|
*
|
||||||
|
* 封装设备类型检测,提供简洁的 API 供组件使用
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const { isMobile, isTablet, isDesktop, deviceType } = useDevice();
|
||||||
|
*
|
||||||
|
* if (isMobile) return <MobileView />;
|
||||||
|
* if (isTablet) return <TabletView />;
|
||||||
|
* return <DesktopView />;
|
||||||
|
*/
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import {
|
||||||
|
selectIsMobile,
|
||||||
|
selectIsTablet,
|
||||||
|
selectIsDesktop,
|
||||||
|
selectDeviceType,
|
||||||
|
} from '@/store/slices/deviceSlice';
|
||||||
|
|
||||||
|
export const useDevice = () => {
|
||||||
|
const isMobile = useSelector(selectIsMobile);
|
||||||
|
const isTablet = useSelector(selectIsTablet);
|
||||||
|
const isDesktop = useSelector(selectIsDesktop);
|
||||||
|
const deviceType = useSelector(selectDeviceType);
|
||||||
|
|
||||||
|
return {
|
||||||
|
isMobile,
|
||||||
|
isTablet,
|
||||||
|
isDesktop,
|
||||||
|
deviceType,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDevice;
|
||||||
@@ -9,6 +9,7 @@ import { usePostHogTrack } from '@/hooks/usePostHogRedux';
|
|||||||
import { useHomeResponsive } from '@/hooks/useHomeResponsive';
|
import { useHomeResponsive } from '@/hooks/useHomeResponsive';
|
||||||
import { ACQUISITION_EVENTS } from '@/lib/constants';
|
import { ACQUISITION_EVENTS } from '@/lib/constants';
|
||||||
import { CORE_FEATURES } from '@/constants/homeFeatures';
|
import { CORE_FEATURES } from '@/constants/homeFeatures';
|
||||||
|
import { performanceMonitor } from '@/utils/performanceMonitor';
|
||||||
import type { Feature } from '@/types/home';
|
import type { Feature } from '@/types/home';
|
||||||
import { HeroBackground } from './components/HeroBackground';
|
import { HeroBackground } from './components/HeroBackground';
|
||||||
import { HeroHeader } from './components/HeroHeader';
|
import { HeroHeader } from './components/HeroHeader';
|
||||||
@@ -36,6 +37,11 @@ const HomePage: React.FC = () => {
|
|||||||
showDecorations
|
showDecorations
|
||||||
} = useHomeResponsive();
|
} = useHomeResponsive();
|
||||||
|
|
||||||
|
// ⚡ 性能标记:首页组件挂载 = 渲染开始
|
||||||
|
useEffect(() => {
|
||||||
|
performanceMonitor.mark('homepage-render-start');
|
||||||
|
}, []);
|
||||||
|
|
||||||
// PostHog 追踪:页面浏览
|
// PostHog 追踪:页面浏览
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
track(ACQUISITION_EVENTS.LANDING_PAGE_VIEWED, {
|
track(ACQUISITION_EVENTS.LANDING_PAGE_VIEWED, {
|
||||||
@@ -67,6 +73,8 @@ const HomePage: React.FC = () => {
|
|||||||
// 背景图片加载完成回调
|
// 背景图片加载完成回调
|
||||||
const handleImageLoad = useCallback(() => {
|
const handleImageLoad = useCallback(() => {
|
||||||
setImageLoaded(true);
|
setImageLoaded(true);
|
||||||
|
// ⚡ 性能标记:首页渲染完成(背景图片加载完成 = 首屏视觉完整)
|
||||||
|
performanceMonitor.mark('homepage-render-end');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// 特色功能(第一个)
|
// 特色功能(第一个)
|
||||||
|
|||||||
Reference in New Issue
Block a user