- 移动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>
562 lines
14 KiB
Markdown
562 lines
14 KiB
Markdown
# 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
|
||
// 点击事件
|
||
<Button onClick={() => {
|
||
navEvents.trackMenuItemClicked('概念中心', 'dropdown', '/concepts');
|
||
navigate('/concepts');
|
||
}}>
|
||
概念中心
|
||
</Button>
|
||
```
|
||
|
||
#### 模式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 <Button onClick={handleAction}>Submit</Button>;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 集成追踪到组件
|
||
|
||
### 完整集成示例
|
||
|
||
```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 (
|
||
<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. 参数设计
|
||
|
||
#### 必填参数前置
|
||
```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
|
||
<Button onClick={() => {
|
||
featureEvents.trackButtonClicked();
|
||
handleAction();
|
||
}}>
|
||
```
|
||
|
||
### Q3: 如何追踪异步操作的完整流程?
|
||
|
||
**A:** 分别追踪开始、成功、失败:
|
||
|
||
```javascript
|
||
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、角色、订阅状态、使用偏好
|
||
- ❌ **不应包含**:密码、完整邮箱、手机号、支付信息
|
||
|
||
```javascript
|
||
// ✅ 正确
|
||
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:
|
||
|
||
```javascript
|
||
// 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 官方文档](https://posthog.com/docs)
|
||
- [POSTHOG_INTEGRATION.md](./POSTHOG_INTEGRATION.md) - 集成总体说明
|
||
- [constants.js](./src/lib/constants.js) - 所有事件常量定义
|
||
- [usePostHogRedux.js](./src/hooks/usePostHogRedux.js) - 核心追踪Hook
|
||
|
||
---
|
||
|
||
## 📝 版本历史
|
||
|
||
- **v1.0** (2025-10-29): 初始版本,包含13个追踪Hook的完整使用指南
|
||
- **v1.1** (待定): 计划添加P2功能追踪指南
|
||
|
||
---
|
||
|
||
**维护者**: 开发团队
|
||
**最后更新**: 2025-10-29
|