# PostHog 事件追踪开发者指南 ## 📚 目录 1. [快速开始](#快速开始) 2. [Hook使用指南](#hook使用指南) 3. [添加新的追踪Hook](#添加新的追踪hook) 4. [集成追踪到组件](#集成追踪到组件) 5. [最佳实践](#最佳实践) 6. [常见问题](#常见问题) --- ## 🚀 快速开始 ### 当前已有的追踪Hooks | Hook名称 | 用途 | 适用场景 | |---------|------|---------| | `useAuthEvents` | 认证事件 | 注册、登录、登出、微信授权 | | `useStockOverviewEvents` | 个股分析 | 个股页面浏览、图表查看、指标分析 | | `useConceptEvents` | 概念追踪 | 概念浏览、搜索、相关股票查看 | | `useCompanyEvents` | 公司分析 | 公司详情、财务数据、行业对比 | | `useLimitAnalyseEvents` | 涨停分析 | 涨停榜单、筛选、个股详情 | | `useCommunityEvents` | 社区事件 | 新闻浏览、事件追踪、评论互动 | | `useEventDetailEvents` | 事件详情 | 事件分析、时间线、影响评估 | | `useDashboardEvents` | 仪表板 | 自选股、关注事件、评论管理 | | `useTradingSimulationEvents` | 模拟盘 | 下单、持仓、收益追踪 | | `useSearchEvents` | 搜索行为 | 搜索查询、结果点击、筛选 | | `useNavigationEvents` | 导航交互 | 菜单点击、主题切换、Logo点击 | | `useProfileEvents` | 个人资料 | 资料更新、密码修改、账号绑定 | | `useSubscriptionEvents` | 订阅支付 | 定价选择、支付流程、订阅管理 | --- ## 📖 Hook使用指南 ### 1. 基础用法 ```javascript // 第一步:导入Hook import { useSearchEvents } from '../../hooks/useSearchEvents'; // 第二步:在组件中初始化 function SearchComponent() { const searchEvents = useSearchEvents({ context: 'global' }); // 第三步:在事件处理函数中调用追踪方法 const handleSearch = (query) => { searchEvents.trackSearchQuerySubmitted(query, resultCount); // ... 执行搜索逻辑 }; } ``` ### 2. 带参数的Hook初始化 大多数Hook支持配置参数,用于区分不同的使用场景: ```javascript // 搜索Hook - 指定搜索上下文 const searchEvents = useSearchEvents({ context: 'community' // 或 'stock', 'news', 'concept' }); // 个人资料Hook - 指定页面类型 const profileEvents = useProfileEvents({ pageType: 'settings' // 或 'profile', 'security' }); // 导航Hook - 指定组件位置 const navEvents = useNavigationEvents({ component: 'top_nav' // 或 'sidebar', 'footer' }); // 订阅Hook - 传入当前订阅信息 const subscriptionEvents = useSubscriptionEvents({ currentSubscription: { plan: user?.subscription_plan || 'free', status: user?.subscription_status || 'none' } }); ``` ### 3. 常见追踪模式 #### 模式A:简单事件追踪 ```javascript // 点击事件 ``` #### 模式B:成功/失败双向追踪 ```javascript const handleSave = async () => { try { await saveData(); profileEvents.trackProfileUpdated(updatedFields, data); toast({ title: "保存成功" }); } catch (error) { profileEvents.trackProfileUpdateFailed(attemptedFields, error.message); toast({ title: "保存失败" }); } }; ``` #### 模式C:条件追踪 ```javascript const handleSearch = (query, resultCount) => { // 只在有查询词时追踪 if (query) { searchEvents.trackSearchQuerySubmitted(query, resultCount); } // 无结果时自动触发额外追踪 if (resultCount === 0) { // Hook内部已自动追踪 SEARCH_NO_RESULTS } }; ``` --- ## 🔨 添加新的追踪Hook ### 步骤1:创建Hook文件 在 `/src/hooks/` 目录下创建新文件,例如 `useYourFeatureEvents.js`: ```javascript // src/hooks/useYourFeatureEvents.js 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.context - 使用上下文 * @returns {Object} 事件追踪处理函数集合 */ export const useYourFeatureEvents = ({ context = 'default' } = {}) => { const { track } = usePostHogTrack(); /** * 追踪功能操作 * @param {string} actionName - 操作名称 * @param {Object} details - 操作详情 */ const trackFeatureAction = useCallback((actionName, details = {}) => { if (!actionName) { logger.warn('useYourFeatureEvents', 'trackFeatureAction: actionName is required'); return; } track(RETENTION_EVENTS.FEATURE_USED, { feature_name: 'your_feature', action_name: actionName, context, ...details, timestamp: new Date().toISOString(), }); logger.debug('useYourFeatureEvents', '📊 Feature Action Tracked', { actionName, context, }); }, [track, context]); return { trackFeatureAction, // ... 更多追踪方法 }; }; export default useYourFeatureEvents; ``` ### 步骤2:定义事件常量(如需要) 在 `/src/lib/constants.js` 中添加新事件: ```javascript export const RETENTION_EVENTS = { // ... 现有事件 YOUR_FEATURE_VIEWED: 'Your Feature Viewed', YOUR_FEATURE_ACTION: 'Your Feature Action', }; ``` ### 步骤3:在组件中集成 ```javascript import { useYourFeatureEvents } from '../../hooks/useYourFeatureEvents'; function YourComponent() { const featureEvents = useYourFeatureEvents({ context: 'main_page' }); const handleAction = () => { featureEvents.trackFeatureAction('button_clicked', { button_name: 'submit', user_role: user?.role }); }; return ; } ``` --- ## 🎯 集成追踪到组件 ### 完整集成示例 ```javascript // src/views/YourFeature/YourComponent.js import React, { useState, useEffect } from 'react'; import { useYourFeatureEvents } from '../../hooks/useYourFeatureEvents'; export default function YourComponent() { const [data, setData] = useState([]); // 🎯 初始化追踪Hook const featureEvents = useYourFeatureEvents({ context: 'your_feature' }); // 🎯 页面加载时自动追踪 useEffect(() => { featureEvents.trackPageViewed(); }, [featureEvents]); // 🎯 用户操作追踪 const handleItemClick = (item) => { featureEvents.trackItemClicked(item.id, item.name); // ... 业务逻辑 }; // 🎯 表单提交追踪(成功/失败) const handleSubmit = async (formData) => { try { const result = await submitData(formData); featureEvents.trackSubmitSuccess(formData, result); toast({ title: '提交成功' }); } catch (error) { featureEvents.trackSubmitFailed(formData, error.message); toast({ title: '提交失败' }); } }; return (
{data.map(item => (
handleItemClick(item)}> {item.name}
))}
{/* 表单内容 */}
); } ``` --- ## ✅ 最佳实践 ### 1. 命名规范 #### Hook命名 - 使用 `use` 前缀:`useFeatureEvents` - 描述性名称:`useSubscriptionEvents` 而非 `useSubEvents` #### 追踪方法命名 - 使用 `track` 前缀:`trackButtonClicked` - 动词+名词结构:`trackSearchSubmitted`, `trackProfileUpdated` - 明确动作:`trackPaymentSuccessful` 而非 `trackPayment` #### 事件常量命名 - 大写+下划线:`SEARCH_QUERY_SUBMITTED` - 名词+动词结构:`PROFILE_UPDATED`, `PAYMENT_INITIATED` ### 2. 参数设计 #### 必填参数前置 ```javascript // ✅ 好的设计 trackSearchSubmitted(query, resultCount, filters) // ❌ 不好的设计 trackSearchSubmitted(filters, resultCount, query) ``` #### 使用对象参数处理复杂数据 ```javascript // ✅ 好的设计 trackPaymentInitiated({ planName: 'pro', amount: 99, currency: 'CNY', paymentMethod: 'wechat_pay' }) // ❌ 不好的设计 trackPaymentInitiated(planName, amount, currency, paymentMethod) ``` #### 提供默认值 ```javascript const trackAction = useCallback((name, details = {}) => { track(EVENT_NAME, { action_name: name, context: context || 'default', timestamp: new Date().toISOString(), ...details }); }, [track, context]); ``` ### 3. 错误处理 #### 参数验证 ```javascript const trackFeature = useCallback((featureName) => { if (!featureName) { logger.warn('useFeatureEvents', 'trackFeature: featureName is required'); return; } track(EVENTS.FEATURE_USED, { feature_name: featureName }); }, [track]); ``` #### 避免追踪崩溃影响业务 ```javascript const handleAction = async () => { try { // 业务逻辑 const result = await doSomething(); // 追踪放在业务逻辑之后,不影响核心功能 try { featureEvents.trackActionSuccess(result); } catch (trackError) { logger.error('Tracking failed', trackError); // 不抛出错误,不影响用户体验 } } catch (error) { // 业务逻辑错误处理 toast({ title: '操作失败' }); } }; ``` ### 4. 性能优化 #### 使用 useCallback 包装追踪函数 ```javascript const trackAction = useCallback((actionName) => { track(EVENTS.ACTION, { action_name: actionName }); }, [track]); ``` #### 避免在循环中追踪 ```javascript // ❌ 不好的做法 items.forEach(item => { trackItemViewed(item.id); }); // ✅ 好的做法 trackItemsViewed(items.length, items.map(i => i.id)); ``` #### 批量追踪 ```javascript // 一次追踪包含所有信息 trackBatchAction({ action_type: 'bulk_delete', item_count: selectedItems.length, item_ids: selectedItems.map(i => i.id) }); ``` ### 5. 调试支持 #### 使用 logger.debug ```javascript const trackAction = useCallback((actionName) => { track(EVENTS.ACTION, { action_name: actionName }); logger.debug('useFeatureEvents', '📊 Action Tracked', { actionName, context, timestamp: new Date().toISOString() }); }, [track, context]); ``` #### 在开发环境显示追踪信息 ```javascript if (process.env.NODE_ENV === 'development') { console.log('[PostHog Track]', eventName, properties); } ``` --- ## 🐛 常见问题 ### Q1: Hook 内的 useCallback 依赖项应该包含哪些? **A:** 只包含函数内部使用的外部变量: ```javascript const trackAction = useCallback((name) => { // ✅ track 和 context 被使用,需要在依赖项中 track(EVENTS.ACTION, { name, context }); }, [track, context]); // 正确的依赖项 ``` ### Q2: 何时使用自动追踪 vs 手动追踪? **A:** - **自动追踪**:页面浏览、组件挂载时的事件 ```javascript useEffect(() => { featureEvents.trackPageViewed(); }, [featureEvents]); ``` - **手动追踪**:用户主动操作的事件 ```javascript