Files
vf_react/docs/POSTHOG_TRACKING_GUIDE.md
zdl 09db05c448 docs: 将所有文档迁移到 docs/ 目录
- 移动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>
2025-10-30 14:51:22 +08:00

14 KiB
Raw Blame History

PostHog 事件追踪开发者指南

📚 目录

  1. 快速开始
  2. Hook使用指南
  3. 添加新的追踪Hook
  4. 集成追踪到组件
  5. 最佳实践
  6. 常见问题

🚀 快速开始

当前已有的追踪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();
  // ...
}

📊 追踪检查清单

在添加新功能时,确保追踪以下关键点:

  • 页面/组件加载 - 用户到达这个页面
  • 主要操作 - 用户执行的核心功能
  • 成功状态 - 操作成功完成
  • 失败状态 - 操作失败及原因
  • 用户输入 - 搜索、筛选、表单提交(不包含敏感信息)
  • 导航行为 - 点击链接、返回、跳转
  • 关键决策点 - 用户做出选择的时刻
  • 转化漏斗 - 从意向到完成的关键步骤

🔗 相关资源


📝 版本历史

  • v1.0 (2025-10-29): 初始版本包含13个追踪Hook的完整使用指南
  • v1.1 (待定): 计划添加P2功能追踪指南

维护者: 开发团队 最后更新: 2025-10-29