- 移动42个文档文件到 docs/ 目录 - 更新 .gitignore 允许 docs/ 下的 .md 文件 - 删除根目录下的重复文档文件 📁 文档分类: - StockDetailPanel 重构文档(3个) - PostHog 集成文档(6个) - 系统架构和API文档(33个) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
14 KiB
14 KiB
PostHog 事件追踪开发者指南
📚 目录
🚀 快速开始
当前已有的追踪Hooks
| Hook名称 | 用途 | 适用场景 |
|---|---|---|
useAuthEvents |
认证事件 | 注册、登录、登出、微信授权 |
useStockOverviewEvents |
个股分析 | 个股页面浏览、图表查看、指标分析 |
useConceptEvents |
概念追踪 | 概念浏览、搜索、相关股票查看 |
useCompanyEvents |
公司分析 | 公司详情、财务数据、行业对比 |
useLimitAnalyseEvents |
涨停分析 | 涨停榜单、筛选、个股详情 |
useCommunityEvents |
社区事件 | 新闻浏览、事件追踪、评论互动 |
useEventDetailEvents |
事件详情 | 事件分析、时间线、影响评估 |
useDashboardEvents |
仪表板 | 自选股、关注事件、评论管理 |
useTradingSimulationEvents |
模拟盘 | 下单、持仓、收益追踪 |
useSearchEvents |
搜索行为 | 搜索查询、结果点击、筛选 |
useNavigationEvents |
导航交互 | 菜单点击、主题切换、Logo点击 |
useProfileEvents |
个人资料 | 资料更新、密码修改、账号绑定 |
useSubscriptionEvents |
订阅支付 | 定价选择、支付流程、订阅管理 |
📖 Hook使用指南
1. 基础用法
// 第一步:导入Hook
import { useSearchEvents } from '../../hooks/useSearchEvents';
// 第二步:在组件中初始化
function SearchComponent() {
const searchEvents = useSearchEvents({ context: 'global' });
// 第三步:在事件处理函数中调用追踪方法
const handleSearch = (query) => {
searchEvents.trackSearchQuerySubmitted(query, resultCount);
// ... 执行搜索逻辑
};
}
2. 带参数的Hook初始化
大多数Hook支持配置参数,用于区分不同的使用场景:
// 搜索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:简单事件追踪
// 点击事件
<Button onClick={() => {
navEvents.trackMenuItemClicked('概念中心', 'dropdown', '/concepts');
navigate('/concepts');
}}>
概念中心
</Button>
模式B:成功/失败双向追踪
const handleSave = async () => {
try {
await saveData();
profileEvents.trackProfileUpdated(updatedFields, data);
toast({ title: "保存成功" });
} catch (error) {
profileEvents.trackProfileUpdateFailed(attemptedFields, error.message);
toast({ title: "保存失败" });
}
};
模式C:条件追踪
const handleSearch = (query, resultCount) => {
// 只在有查询词时追踪
if (query) {
searchEvents.trackSearchQuerySubmitted(query, resultCount);
}
// 无结果时自动触发额外追踪
if (resultCount === 0) {
// Hook内部已自动追踪 SEARCH_NO_RESULTS
}
};
🔨 添加新的追踪Hook
步骤1:创建Hook文件
在 /src/hooks/ 目录下创建新文件,例如 useYourFeatureEvents.js:
// 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 中添加新事件:
export const RETENTION_EVENTS = {
// ... 现有事件
YOUR_FEATURE_VIEWED: 'Your Feature Viewed',
YOUR_FEATURE_ACTION: 'Your Feature Action',
};
步骤3:在组件中集成
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 <Button onClick={handleAction}>Submit</Button>;
}
🎯 集成追踪到组件
完整集成示例
// 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 (
<div>
{data.map(item => (
<div key={item.id} onClick={() => handleItemClick(item)}>
{item.name}
</div>
))}
<form onSubmit={handleSubmit}>
{/* 表单内容 */}
</form>
</div>
);
}
✅ 最佳实践
1. 命名规范
Hook命名
- 使用
use前缀:useFeatureEvents - 描述性名称:
useSubscriptionEvents而非useSubEvents
追踪方法命名
- 使用
track前缀:trackButtonClicked - 动词+名词结构:
trackSearchSubmitted,trackProfileUpdated - 明确动作:
trackPaymentSuccessful而非trackPayment
事件常量命名
- 大写+下划线:
SEARCH_QUERY_SUBMITTED - 名词+动词结构:
PROFILE_UPDATED,PAYMENT_INITIATED
2. 参数设计
必填参数前置
// ✅ 好的设计
trackSearchSubmitted(query, resultCount, filters)
// ❌ 不好的设计
trackSearchSubmitted(filters, resultCount, query)
使用对象参数处理复杂数据
// ✅ 好的设计
trackPaymentInitiated({
planName: 'pro',
amount: 99,
currency: 'CNY',
paymentMethod: 'wechat_pay'
})
// ❌ 不好的设计
trackPaymentInitiated(planName, amount, currency, paymentMethod)
提供默认值
const trackAction = useCallback((name, details = {}) => {
track(EVENT_NAME, {
action_name: name,
context: context || 'default',
timestamp: new Date().toISOString(),
...details
});
}, [track, context]);
3. 错误处理
参数验证
const trackFeature = useCallback((featureName) => {
if (!featureName) {
logger.warn('useFeatureEvents', 'trackFeature: featureName is required');
return;
}
track(EVENTS.FEATURE_USED, { feature_name: featureName });
}, [track]);
避免追踪崩溃影响业务
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 包装追踪函数
const trackAction = useCallback((actionName) => {
track(EVENTS.ACTION, { action_name: actionName });
}, [track]);
避免在循环中追踪
// ❌ 不好的做法
items.forEach(item => {
trackItemViewed(item.id);
});
// ✅ 好的做法
trackItemsViewed(items.length, items.map(i => i.id));
批量追踪
// 一次追踪包含所有信息
trackBatchAction({
action_type: 'bulk_delete',
item_count: selectedItems.length,
item_ids: selectedItems.map(i => i.id)
});
5. 调试支持
使用 logger.debug
const trackAction = useCallback((actionName) => {
track(EVENTS.ACTION, { action_name: actionName });
logger.debug('useFeatureEvents', '📊 Action Tracked', {
actionName,
context,
timestamp: new Date().toISOString()
});
}, [track, context]);
在开发环境显示追踪信息
if (process.env.NODE_ENV === 'development') {
console.log('[PostHog Track]', eventName, properties);
}
🐛 常见问题
Q1: Hook 内的 useCallback 依赖项应该包含哪些?
A: 只包含函数内部使用的外部变量:
const trackAction = useCallback((name) => {
// ✅ track 和 context 被使用,需要在依赖项中
track(EVENTS.ACTION, {
name,
context
});
}, [track, context]); // 正确的依赖项
Q2: 何时使用自动追踪 vs 手动追踪?
A:
-
自动追踪:页面浏览、组件挂载时的事件
useEffect(() => { featureEvents.trackPageViewed(); }, [featureEvents]); -
手动追踪:用户主动操作的事件
<Button onClick={() => { featureEvents.trackButtonClicked(); handleAction(); }}>
Q3: 如何追踪异步操作的完整流程?
A: 分别追踪开始、成功、失败:
const handleAsyncAction = async () => {
// 1. 追踪开始
featureEvents.trackActionStarted();
try {
const result = await doAsyncWork();
// 2. 追踪成功
featureEvents.trackActionSuccess(result);
} catch (error) {
// 3. 追踪失败
featureEvents.trackActionFailed(error.message);
}
};
Q4: 追踪中应该包含哪些用户信息?
A:
- ✅ 可以包含:用户ID、角色、订阅状态、使用偏好
- ❌ 不应包含:密码、完整邮箱、手机号、支付信息
// ✅ 正确
track(EVENT, {
user_id: user.id,
user_role: user.role,
subscription_tier: user.subscription_tier
});
// ❌ 错误
track(EVENT, {
password: user.password, // 绝对不要追踪密码
email: user.email, // 避免完整邮箱
credit_card: '****1234' // 不追踪支付信息
});
Q5: 如何在多个组件间共享追踪逻辑?
A: 使用自定义Hook:
// hooks/useCommonTracking.js
export const useCommonTracking = () => {
const { track } = usePostHogTrack();
const trackError = useCallback((errorMessage, errorCode) => {
track('Error Occurred', {
error_message: errorMessage,
error_code: errorCode,
timestamp: new Date().toISOString()
});
}, [track]);
return { trackError };
};
// 在多个组件中使用
function ComponentA() {
const { trackError } = useCommonTracking();
// ...
}
function ComponentB() {
const { trackError } = useCommonTracking();
// ...
}
📊 追踪检查清单
在添加新功能时,确保追踪以下关键点:
- 页面/组件加载 - 用户到达这个页面
- 主要操作 - 用户执行的核心功能
- 成功状态 - 操作成功完成
- 失败状态 - 操作失败及原因
- 用户输入 - 搜索、筛选、表单提交(不包含敏感信息)
- 导航行为 - 点击链接、返回、跳转
- 关键决策点 - 用户做出选择的时刻
- 转化漏斗 - 从意向到完成的关键步骤
🔗 相关资源
- PostHog 官方文档
- POSTHOG_INTEGRATION.md - 集成总体说明
- constants.js - 所有事件常量定义
- usePostHogRedux.js - 核心追踪Hook
📝 版本历史
- v1.0 (2025-10-29): 初始版本,包含13个追踪Hook的完整使用指南
- v1.1 (待定): 计划添加P2功能追踪指南
维护者: 开发团队 最后更新: 2025-10-29