1. 完整的用户旅程
- 从进入网站 → 浏览内容 → 使用功能 → 遇到付费墙 → 付费转化
2. 核心业务指标
- DAU/MAU(活跃用户)
- 功能使用率(哪些功能最受欢迎)
- 搜索热度(用户需求洞察)
- Revenue转化漏斗(付费转化分析)
- 用户参与度(Profile更新、设置变更)
3. 产品优化方向
- 哪些功能需要优化?
- 用户在哪个环节流失?
- 哪些内容最受欢迎?
- 如何提高付费转化率?
294 lines
7.9 KiB
JavaScript
294 lines
7.9 KiB
JavaScript
// src/hooks/useNavigationEvents.js
|
||
// 导航和菜单事件追踪 Hook
|
||
|
||
import { useCallback } from 'react';
|
||
import { usePostHogTrack } from './usePostHogRedux';
|
||
import { RETENTION_EVENTS } from '../lib/constants';
|
||
import { logger } from '../utils/logger';
|
||
|
||
/**
|
||
* 导航事件追踪 Hook
|
||
* @param {Object} options - 配置选项
|
||
* @param {string} options.component - 组件名称 ('top_nav' | 'sidebar' | 'breadcrumb' | 'footer')
|
||
* @returns {Object} 事件追踪处理函数集合
|
||
*/
|
||
export const useNavigationEvents = ({ component = 'navigation' } = {}) => {
|
||
const { track } = usePostHogTrack();
|
||
|
||
/**
|
||
* 追踪顶部导航点击
|
||
* @param {string} itemName - 导航项名称
|
||
* @param {string} path - 导航目标路径
|
||
* @param {string} category - 导航分类 ('main' | 'user' | 'utility')
|
||
*/
|
||
const trackTopNavClicked = useCallback((itemName, path = '', category = 'main') => {
|
||
if (!itemName) {
|
||
logger.warn('useNavigationEvents', 'trackTopNavClicked: itemName is required');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.TOP_NAV_CLICKED, {
|
||
item_name: itemName,
|
||
path,
|
||
category,
|
||
component,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useNavigationEvents', '🔝 Top Navigation Clicked', {
|
||
itemName,
|
||
path,
|
||
category,
|
||
});
|
||
}, [track, component]);
|
||
|
||
/**
|
||
* 追踪侧边栏菜单点击
|
||
* @param {string} itemName - 菜单项名称
|
||
* @param {string} path - 目标路径
|
||
* @param {number} level - 菜单层级 (1=主菜单, 2=子菜单)
|
||
* @param {boolean} isExpanded - 是否展开状态
|
||
*/
|
||
const trackSidebarMenuClicked = useCallback((itemName, path = '', level = 1, isExpanded = false) => {
|
||
if (!itemName) {
|
||
logger.warn('useNavigationEvents', 'trackSidebarMenuClicked: itemName is required');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.SIDEBAR_MENU_CLICKED, {
|
||
item_name: itemName,
|
||
path,
|
||
level,
|
||
is_expanded: isExpanded,
|
||
component,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useNavigationEvents', '📂 Sidebar Menu Clicked', {
|
||
itemName,
|
||
path,
|
||
level,
|
||
isExpanded,
|
||
});
|
||
}, [track, component]);
|
||
|
||
/**
|
||
* 追踪通用菜单项点击
|
||
* @param {string} itemName - 菜单项名称
|
||
* @param {string} menuType - 菜单类型 ('dropdown' | 'context' | 'tab')
|
||
* @param {string} path - 目标路径
|
||
*/
|
||
const trackMenuItemClicked = useCallback((itemName, menuType = 'dropdown', path = '') => {
|
||
if (!itemName) {
|
||
logger.warn('useNavigationEvents', 'trackMenuItemClicked: itemName is required');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.MENU_ITEM_CLICKED, {
|
||
item_name: itemName,
|
||
menu_type: menuType,
|
||
path,
|
||
component,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useNavigationEvents', '📋 Menu Item Clicked', {
|
||
itemName,
|
||
menuType,
|
||
path,
|
||
});
|
||
}, [track, component]);
|
||
|
||
/**
|
||
* 追踪面包屑导航点击
|
||
* @param {string} itemName - 面包屑项名称
|
||
* @param {string} path - 目标路径
|
||
* @param {number} position - 在面包屑中的位置
|
||
* @param {number} totalItems - 面包屑总项数
|
||
*/
|
||
const trackBreadcrumbClicked = useCallback((itemName, path = '', position = 0, totalItems = 0) => {
|
||
if (!itemName) {
|
||
logger.warn('useNavigationEvents', 'trackBreadcrumbClicked: itemName is required');
|
||
return;
|
||
}
|
||
|
||
track(RETENTION_EVENTS.BREADCRUMB_CLICKED, {
|
||
item_name: itemName,
|
||
path,
|
||
position,
|
||
total_items: totalItems,
|
||
is_last: position === totalItems - 1,
|
||
component,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useNavigationEvents', '🍞 Breadcrumb Clicked', {
|
||
itemName,
|
||
position,
|
||
totalItems,
|
||
});
|
||
}, [track, component]);
|
||
|
||
/**
|
||
* 追踪Logo点击(返回首页)
|
||
*/
|
||
const trackLogoClicked = useCallback(() => {
|
||
track('Logo Clicked', {
|
||
component,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useNavigationEvents', '🏠 Logo Clicked');
|
||
}, [track, component]);
|
||
|
||
/**
|
||
* 追踪用户菜单展开
|
||
* @param {Object} user - 用户对象
|
||
* @param {number} menuItemCount - 菜单项数量
|
||
*/
|
||
const trackUserMenuOpened = useCallback((user = {}, menuItemCount = 0) => {
|
||
track('User Menu Opened', {
|
||
user_id: user.id || null,
|
||
menu_item_count: menuItemCount,
|
||
component,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useNavigationEvents', '👤 User Menu Opened', {
|
||
userId: user.id,
|
||
menuItemCount,
|
||
});
|
||
}, [track, component]);
|
||
|
||
/**
|
||
* 追踪通知中心打开
|
||
* @param {number} unreadCount - 未读通知数量
|
||
*/
|
||
const trackNotificationCenterOpened = useCallback((unreadCount = 0) => {
|
||
track('Notification Center Opened', {
|
||
unread_count: unreadCount,
|
||
has_unread: unreadCount > 0,
|
||
component,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useNavigationEvents', '🔔 Notification Center Opened', {
|
||
unreadCount,
|
||
});
|
||
}, [track, component]);
|
||
|
||
/**
|
||
* 追踪语言切换
|
||
* @param {string} fromLanguage - 原语言
|
||
* @param {string} toLanguage - 目标语言
|
||
*/
|
||
const trackLanguageChanged = useCallback((fromLanguage, toLanguage) => {
|
||
if (!fromLanguage || !toLanguage) {
|
||
logger.warn('useNavigationEvents', 'trackLanguageChanged: both languages are required');
|
||
return;
|
||
}
|
||
|
||
track('Language Changed', {
|
||
from_language: fromLanguage,
|
||
to_language: toLanguage,
|
||
component,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useNavigationEvents', '🌐 Language Changed', {
|
||
fromLanguage,
|
||
toLanguage,
|
||
});
|
||
}, [track, component]);
|
||
|
||
/**
|
||
* 追踪主题切换(深色/浅色模式)
|
||
* @param {string} fromTheme - 原主题
|
||
* @param {string} toTheme - 目标主题
|
||
*/
|
||
const trackThemeChanged = useCallback((fromTheme, toTheme) => {
|
||
if (!fromTheme || !toTheme) {
|
||
logger.warn('useNavigationEvents', 'trackThemeChanged: both themes are required');
|
||
return;
|
||
}
|
||
|
||
track('Theme Changed', {
|
||
from_theme: fromTheme,
|
||
to_theme: toTheme,
|
||
component,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useNavigationEvents', '🎨 Theme Changed', {
|
||
fromTheme,
|
||
toTheme,
|
||
});
|
||
}, [track, component]);
|
||
|
||
/**
|
||
* 追踪快捷键使用
|
||
* @param {string} shortcut - 快捷键组合 (如 'Ctrl+K', 'Cmd+/')
|
||
* @param {string} action - 触发的动作
|
||
*/
|
||
const trackShortcutUsed = useCallback((shortcut, action = '') => {
|
||
if (!shortcut) {
|
||
logger.warn('useNavigationEvents', 'trackShortcutUsed: shortcut is required');
|
||
return;
|
||
}
|
||
|
||
track('Keyboard Shortcut Used', {
|
||
shortcut,
|
||
action,
|
||
component,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useNavigationEvents', '⌨️ Keyboard Shortcut Used', {
|
||
shortcut,
|
||
action,
|
||
});
|
||
}, [track, component]);
|
||
|
||
/**
|
||
* 追踪返回按钮点击
|
||
* @param {string} fromPage - 当前页面
|
||
* @param {string} toPage - 返回到的页面
|
||
*/
|
||
const trackBackButtonClicked = useCallback((fromPage = '', toPage = '') => {
|
||
track('Back Button Clicked', {
|
||
from_page: fromPage,
|
||
to_page: toPage,
|
||
component,
|
||
timestamp: new Date().toISOString(),
|
||
});
|
||
|
||
logger.debug('useNavigationEvents', '◀️ Back Button Clicked', {
|
||
fromPage,
|
||
toPage,
|
||
});
|
||
}, [track, component]);
|
||
|
||
return {
|
||
// 导航点击事件
|
||
trackTopNavClicked,
|
||
trackSidebarMenuClicked,
|
||
trackMenuItemClicked,
|
||
trackBreadcrumbClicked,
|
||
trackLogoClicked,
|
||
|
||
// 用户交互事件
|
||
trackUserMenuOpened,
|
||
trackNotificationCenterOpened,
|
||
|
||
// 设置变更事件
|
||
trackLanguageChanged,
|
||
trackThemeChanged,
|
||
|
||
// 其他交互
|
||
trackShortcutUsed,
|
||
trackBackButtonClicked,
|
||
};
|
||
};
|
||
|
||
export default useNavigationEvents;
|