PostHogAnalytics技能 posthog-analytics

PostHog Analytics 技能是一套用于实施产品分析的工具,包括事件跟踪、用户识别、功能标志和项目特定仪表板,旨在帮助开发者和产品经理深入了解用户行为和产品性能,优化用户体验。

数据分析 0 次安装 0 次浏览 更新于 3/5/2026

PostHog Analytics 技能

使用基础.md + [框架].md 加载

实现 PostHog 产品分析 - 事件跟踪、用户识别、功能标志和项目特定仪表板。

来源: PostHog 文档 | 产品分析 | 功能标志


理念

衡量重要之事,而非一切。

分析应该回答具体问题:

  • 用户是否获得价值?(激活、留存)
  • 用户在哪里挣扎?(漏斗、流失点)
  • 哪些功能推动参与度?(功能使用)
  • 产品是否在增长?(获取、推荐)

不要追踪一切。追踪那些能够指导决策的信息。


安装

Next.js (App Router)

npm install posthog-js
// lib/posthog.ts
import posthog from 'posthog-js';

export function initPostHog() {
  if (typeof window !== 'undefined' && !posthog.__loaded) {
    posthog.init(process.env.NEXT_PUBLIC_POSTHOG_KEY!, {
      api_host: process.env.NEXT_PUBLIC_POSTHOG_HOST || 'https://us.i.posthog.com',
      person_profiles: 'identified_only', // 仅为已识别用户创建档案
      capture_pageview: false, // 我们将手动处理 SPA 的这个功能
      capture_pageleave: true,
      loaded: (posthog) => {
        if (process.env.NODE_ENV === 'development') {
          posthog.debug();
        }
      },
    });
  }
  return posthog;
}

export { posthog };
// app/providers.tsx
'use client';

import { useEffect } from 'react';
import { usePathname, useSearchParams } from 'next/navigation';
import { initPostHog, posthog } from '@/lib/posthog';

export function PostHogProvider({ children }: { children: React.ReactNode }) {
  const pathname = usePathname();
  const searchParams = useSearchParams();

  useEffect(() => {
    initPostHog();
  }, []);

  // 跟踪页面浏览
  useEffect(() => {
    if (pathname) {
      let url = window.origin + pathname;
      if (searchParams.toString()) {
        url += `?${searchParams.toString()}`;
      }
      posthog.capture('$pageview', { $current_url: url });
    }
  }, [pathname, searchParams]);

  return <>{children}</>;
}
// app/layout.tsx
import { PostHogProvider } from './providers';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh">
      <body>
        <PostHogProvider>
          {children}
        </PostHogProvider>
      </body>
    </html>
  );
}

React (Vite/CRA)

// src/posthog.ts
import posthog from 'posthog-js';

posthog.init(import.meta.env.VITE_POSTHOG_KEY, {
  api_host: import.meta.env.VITE_POSTHOG_HOST || 'https://us.i.posthog.com',
  person_profiles: 'identified_only',
});

export { posthog };
// src/main.tsx
import { PostHogProvider } from 'posthog-js/react';
import { posthog } from './posthog';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <PostHogProvider client={posthog}>
    <App />
  </PostHogProvider>
);

Python (FastAPI/Flask)

pip install posthog
# analytics/posthog_client.py
import posthog
from functools import lru_cache

@lru_cache()
def get_posthog():
    posthog.project_api_key = os.environ["POSTHOG_API_KEY"]
    posthog.host = os.environ.get("POSTHOG_HOST", "https://us.i.posthog.com")
    posthog.debug = os.environ.get("ENV") == "development"
    return posthog

# 使用方法
def track_event(user_id: str, event: str, properties: dict = None):
    ph = get_posthog()
    ph.capture(
        distinct_id=user_id,
        event=event,
        properties=properties or {}
    )

def identify_user(user_id: str, properties: dict):
    ph = get_posthog()
    ph.identify(user_id, properties)

Node.js (Express/Hono)

npm install posthog-node
// lib/posthog.ts
import { PostHog } from 'posthog-node';

const posthog = new PostHog(process.env.POSTHog_API_KEY!, {
  host: process.env.POSTHOG_HOST || 'https://us.i.posthog.com',
});

// 终止时刷新
process.on('SIGTERM', () => posthog.shutdown());

export { posthog };

// 使用方法
export function trackEvent(userId: string, event: string, properties?: Record<string, any>) {
  posthog.capture({
    distinctId: userId,
    event,
    properties,
  });
}

export function identifyUser(userId: string, properties: Record<string, any>) {
  posthog.identify({
    distinctId: userId,
    properties,
  });
}

环境变量

# .env.local (Next.js) - 安全:这些应该是公开的
NEXT_PUBLIC_POSTHOG_KEY=phc_xxxxxxxxxxxxxxxxxxxx
NEXT_PUBLIC_POSTHOG_HOST=https://us.i.posthog.com

# .env (后端) - 保密
POSTHOG_API_KEY=phc_xxxxxxxxxxxxxxxxxxxx
POSTHOG_HOST=https://us.i.posthog.com

添加到 credentials.md 模式:

'POSTHOG_API_KEY': r'phc_[A-Za-z0-9]+',

用户识别

何时识别

// 注册时识别
async function handleSignup(email: string, name: string) {
  const user = await createUser(email, name);

  posthog.identify(user.id, {
    email: user.email,
    name: user.name,
    created_at: user.createdAt,
    plan: 'free',
  });

  posthog.capture('user_signed_up', {
    signup_method: 'email',
  });
}

// 登录时识别
async function handleLogin(email: string) {
  const user = await authenticateUser(email);

  posthog.identify(user.id, {
    email: user.email,
    name: user.name,
    plan: user.plan,
    last_login: new Date().toISOString(),
  });

  posthog.capture('user_logged_in');
}

// 注销时重置
function handleLogout() {
  posthog.capture('user_logged_out');
  posthog.reset(); // 清除身份
}

用户属性

// 跟踪的标准属性
interface UserProperties {
  // 身份
  email: string;
  name: string;

  // 生命周期
  created_at: string;
  plan: 'free' | 'pro' | 'enterprise';

  // 参与度
  onboarding_completed: boolean;
  feature_count: number;

  // 商业
  company_name?: string;
  company_size?: string;
  industry?: string;
}

// 当属性变化时更新属性
posthog.capture('$set', {
  $set: { plan: 'pro' },
});

事件跟踪模式

事件命名约定

// 格式:[对象]_[动作]
// 使用 snake_case,过去时用于动作

// ✅ 好的事件名称
'user_signed_up'
'feature_created'
'subscription_upgraded'
'onboarding_completed'
'invite_sent'
'file_uploaded'
'search_performed'
'checkout_started'
'payment_completed'

// ❌ 坏的事件名称
'click'           // 太模糊
'ButtonClick'     // 不是 snake_case
'user signup'     // 有空格
'creatingFeature' // 不是过去时

按类别划分的核心事件

// === 认证 ===
posthog.capture('user_signed_up', {
  signup_method: 'google' | 'email' | 'github',
  referral_source: 'organic' | 'paid' | 'referral',
});

posthog.capture('user_logged_in', {
  login_method: 'google' | 'email' | 'magic_link',
});

posthog.capture('user_logged_out');

posthog.capture('password_reset_requested');

// === 引导 ===
posthog.capture('onboarding_started');

posthog.capture('onboarding_step_completed', {
  step_name: 'profile' | 'preferences' | 'first_action',
  step_number: 1,
  total_steps: 3,
});

posthog.capture('onboarding_completed', {
  duration_seconds: 120,
  steps_skipped: 0,
});

posthog.capture('onboarding_skipped', {
  skipped_at_step: 2,
});

// === 功能使用 ===
posthog.capture('feature_used', {
  feature_name: 'export' | 'share' | 'duplicate',
  context: 'dashboard' | 'editor',
});

posthog.capture('[resource]_created', {
  resource_type: 'project' | 'document' | 'team',
  // 特定于资源的属性
});

posthog.capture('[resource]_updated', {
  resource_type: 'project',
  fields_changed: ['name', 'description'],
});

posthog.capture('[resource]_deleted', {
  resource_type: 'project',
});

// === 账单 ===
posthog.capture('pricing_page_viewed', {
  current_plan: 'free',
});

posthog.capture('checkout_started', {
  plan: 'pro',
  billing_period: 'monthly' | 'annual',
  price: 29,
});

posthog.capture('subscription_upgraded', {
  from_plan: 'free',
  to_plan: 'pro',
  mrr_change: 29,
});

posthog.capture('subscription_downgraded', {
  from_plan: 'pro',
  to_plan: 'free',
  reason: 'too_expensive' | 'missing_features' | 'not_using',
});

posthog.capture('subscription_cancelled', {
  plan: 'pro',
  reason: 'string',
  feedback: 'string',
});

// === 错误 ===
posthog.capture('error_occurred', {
  error_type: 'api_error' | 'validation_error' | 'network_error',
  error_message: 'string',
  error_code: 'string',
  page: '/dashboard',
});

React 钩子用于跟踪

// hooks/useTrack.ts
import { useCallback } from 'react';
import { posthog } from '@/lib/posthog';

export function useTrack() {
  const track = useCallback((event: string, properties?: Record<string, any>) => {
    posthog.capture(event, {
      ...properties,
      timestamp: new Date().toISOString(),
    });
  }, []);

  return { track };
}

// 使用方法
function CreateProjectButton() {
  const { track } = useTrack();

  const handleCreate = async () => {
    track('project_creation_started');

    try {
      const project = await createProject();
      track('project_created', {
        project_id: project.id,
        template_used: project.template,
      });
    } catch (error) {
      track('project_creation_failed', {
        error_message: error.message,
      });
    }
  };

  return <button onClick={handleCreate}>Create Project</button>;
}

功能标志

设置

// 检查功能标志(客户端)
import { useFeatureFlagEnabled } from 'posthog-js/react';

function NewFeature() {
  const showNewUI = useFeatureFlagEnabled('new-dashboard-ui');

  if (showNewUI) {
    return <NewDashboard />;
  }
  return <OldDashboard />;
}

// 带载荷
import { useFeatureFlagPayload } from 'posthog-js/react';

function PricingPage() {
  const pricingConfig = useFeatureFlagPayload('pricing-experiment');
  // pricingConfig = { price: 29, showAnnual: true }

  return <Pricing config={pricingConfig} />;
}

服务器端(Next.js)

// app/dashboard/page.tsx
import { PostHog } from 'posthog-node';
import { cookies } from 'next/headers';

async function getFeatureFlags(userId: string) {
  const posthog = new PostHog(process.env.POSTHOG_API_KEY!);

  const flags = await posthog.getAllFlags(userId);
  await posthog.shutdown();

  return flags;
}

export default async function Dashboard() {
  const cookieStore = cookies();
  const userId = cookieStore.get('user_id')?.value;

  const flags = await getFeatureFlags(userId);

  return (
    <div>
      {flags['new-dashboard'] && <NewFeature />}
    </div>
  );
}

A/B 测试

// 跟踪实验曝光
function ExperimentComponent() {
  const variant = useFeatureFlagEnabled('checkout-experiment');

  useEffect(() => {
    posthog.capture('experiment_viewed', {
      experiment: 'checkout-experiment',
      variant: variant ? 'test' : 'control',
    });
  }, [variant]);

  return variant ? <NewCheckout /> : <OldCheckout />;
}

项目特定仪表板

SaaS 产品

## SaaS 仪表板必备

### 1. 获取仪表板
**问题解答:** 用户从何而来?什么转化?

创建见解:
- [ ] 按来源注册(每日/每周趋势)
- [ ] 注册转化率按着陆页
- [ ] 从首次访问到注册的时间
- [ ] 注册漏斗:访问 → 注册页面 → 表单开始 → 完成

### 2. 激活仪表板
**问题解答:** 新用户是否获得价值?

创建见解:
- [ ] 引导完成率
- [ ] 首次关键动作的时间
- [ ] 激活率(% 在前 7 天内达到 "aha 时刻")
- [ ] 按引导步骤的流失
- [ ] 第一次会话中的功能采用

### 3. 参与仪表板
**问题解答:** 用户如何使用产品?

创建见解:
- [ ] DAU/WAU/MAU 趋势
- [ ] 功能使用热图
- [ ] 会话持续时间分布
- [ ] 每次会话的动作
- [ ] 强力用户与普通用户

### 4. 留存仪表板
**问题解答:** 用户是否回来?

创建见解:
- [ ] 留存队列(D1, D7, D30)
- [ ] 按计划的流失率
- [ ] 重新激活率
- [ ] 流失前最后的动作
- [ ] 与留存相关的功能

### 5. 收入仪表板
**问题解答:** 业务是否在增长?

创建见解:
- [ ] MRR 趋势
- [ ] 升级与降级
- [ ] 试用到付费转化
- [ ] 按计划的收入
- [ ] 按获取来源的 LTV

电子商务

## 电子商务仪表板必备

### 1. 转化漏斗
创建见解:
- [ ] 全漏斗:浏览 → PDP → 添加到购物车 → 结账 → 购买
- [ ] 购物车放弃率
- [ ] 结账放弃率按步骤
- [ ] 支付失败率

### 2. 产品性能
创建见解:
- [ ] 按产品查看 → 购买(按产品)
- [ ] 按类别添加到购物车率
- [ ] 搜索 → 购买相关性
- [ ] 交叉销售效果

### 3. 客户仪表板
创建见解:
- [ ] 重复购买率
- [ ] 平均订单价值趋势
- [ ] 客户终身价值
- [ ] 购买频率分布

内容/媒体

## 内容仪表板必备

### 1. 消费仪表板
创建见解:
- [ ] 按类型查看内容
- [ ] 阅读/观看完成率
- [ ] 内容上的时间
- [ ] 滚动深度分布

### 2. 参与仪表板
创建见解:
- [ ] 按内容分享
- [ ] 每篇文章的评论
- [ ] 保存/书签率
- [ ] 回访相同内容

### 3. 增长仪表板
创建见解:
- [ ] 新访客与回访者
- [ ] 电子邮件注册率
- [ ] 引荐流量来源

AI/LLM 应用

## AI 应用仪表板必备

### 1. 使用仪表板
创建见解:
- [ ] 用户每天查询次数
- [ ] 令牌使用分布
- [ ] 响应时间 p50/p95
- [ ] 按查询类型的错误率

### 2. 质量仪表板
创建见解:
- [ ] 用户反馈(点赞/点踩)
- [ ] 再生率(用户要求新响应)
- [ ] 编辑率(用户修改 AI 输出)
- [ ] 后续查询率

### 3. 成本仪表板
创建见解:
- [ ] 用户令牌成本
- [ ] 按模型成本
- [ ] 按功能成本
- [ ] 效率趋势(价值/成本)

创建仪表板

使用 PostHog MCP

当为项目设置分析时:

1. 首先,检查现有仪表板:
   - 使用 `dashboards-get-all` 列出当前仪表板

2. 创建适合项目的仪表板:
   - 使用 `dashboard-create` 创建具有描述性的名称

3. 为每个仪表板创建见解:
   - 使用 `query-run` 测试查询
   - 使用 `insight-create-from-query` 保存
   - 使用 `add-insight-to-dashboard` 组织

4. 设置关键漏斗:
   - 注册漏斗
   - 引导漏斗
   - 购买/转化漏斗

仪表板创建工作流程

// 示例:通过 MCP 创建 SaaS 仪表板

// 1. 创建仪表板
const dashboard = await mcp_posthog_dashboard_create({
  name: "激活指标",
  description: "跟踪新用户激活和引导",
  tags: ["saas", "activation"],
});

// 2. 创建见解
const signupFunnel = await mcp_posthog_query_run({
  query: {
    kind: "InsightVizNode",
    source: {
      kind: "FunnelsQuery",
      series: [
        { kind: "EventsNode", event: "user_signed_up", name: "已注册" },
        { kind: "EventsNode", event: "onboarding_started", name: "开始引导" },
        { kind: "EventsNode", event: "onboarding_completed", name: "完成引导" },
        { kind: "EventsNode", event: "first_value_action", name: "首次价值" },
      ],
      dateRange: { date_from: "-30d" },
    },
  },
});

// 3. 保存并添加到仪表板
const insight = await mcp_posthog_insight_create_from_query({
  name: "注册到激活漏斗",
  query: signupFunnel.query,
  favorited: true,
});

await mcp_posthog_add_insight_to_dashboard({
  insightId: insight.id,
  dashboardId: dashboard.id,
});

隐私与合规

GDPR 合规

// 处理退出选项
export function handleCookieConsent(consent: boolean) {
  if (consent) {
    posthog.opt_in_capturing();
  } else {
    posthog.opt_out_capturing();
  }
}

// 检查同意状态
const hasConsent = posthog.has_opted_in_capturing();

// 初始化时检查同意
posthog.init(key, {
  opt_out_capturing_by_default: true, // 需要明确的同意
  respect_dnt: true, // 尊重不追踪
});

永不跟踪的数据

// ❌ 永不跟踪这些
posthog.capture('event', {
  password: '...',           // 凭据
  credit_card: '...',        // 支付信息
  ssn: '...',                // 政府 ID
  medical_info: '...',       // 健康数据
  full_address: '...',       // 详细位置
});

// ✅ 可以跟踪
posthog.capture('event', {
  country: 'US',             // 一般位置
  plan: 'pro',               // 产品信息
  feature_used: 'export',    // 使用情况
});

属性清理

// lib/analytics.ts
const SENSITIVE_KEYS = ['password', 'token', 'secret', 'credit', 'ssn'];

function sanitizeProperties(props: Record<string, any>): Record<string, any> {
  return Object.fromEntries(
    Object.entries(props).filter(([key]) =>
      !SENSITIVE_KEYS.some(sensitive => key.toLowerCase().includes(sensitive))
    )
  );
}

export function safeCapture(event: string, properties?: Record<string, any>) {
  posthog.capture(event, sanitizeProperties(properties || {}));
}

测试分析

开发模式

// 在开发中禁用
if (process.env.NODE_ENV === 'development') {
  posthog.opt_out_capturing();
  // 或使用调试模式
  posthog.debug();
}

E2E 测试

// playwright/fixtures.ts
import { test as base } from '@playwright/test';

export const test = base.extend({
  page: async ({ page }, use) => {
    // 模拟 PostHog 捕获事件
    await page.addInitScript(() => {
      window.capturedEvents = [];
      window.posthog = {
        capture: (event, props) => {
          window.capturedEvents.push({ event, props });
        },
        identify: () => {},
        reset: () => {},
      };
    });
    await use(page);
  },
});

// 在测试中
test('跟踪注册事件', async ({ page }) => {
  await page.goto('/signup');
  await page.fill('[name=email]', 'test@example.com');
  await page.click('button[type=submit]');

  const events = await page.evaluate(() => window.capturedEvents);
  expect(events).toContainEqual({
    event: 'user_signed_up',
    props: expect.objectContaining({ signup_method: 'email' }),
  });
});

调试

PostHog 工具栏

// 启用工具栏进行调试
posthog.init(key, {
  // ...
  loaded: (posthog) => {
    if (process.env.NODE_ENV === 'development') {
      posthog.debug();
      // 工具栏可通过 PostHog 仪表板获得
    }
  },
});

事件调试

// 在开发中记录所有事件
posthog.init(key, {
  _onCapture: (eventName, eventData) => {
    if (process.env.NODE_ENV === 'development') {
      console.log('PostHog 事件:', eventName, eventData);
    }
  },
});

快速参考

用户生命周期事件清单

## 必须跟踪的事件

### 获取
- [ ] `page_viewed`(自动捕获页面浏览)
- [ ] `user_signed_up`
- [ ] `user_logged_in`

### 激活
- [ ] `onboarding_started`
- [ ] `onboarding_step_completed`
- [ ] `onboarding_completed`
- [ ] `first_[key_action]`(你的 "aha 时刻")

### 参与度
- [ ] `[feature]_used`
- [ ] `[resource]_created`
- [ ] `search_performed`
- [ ] `invite_sent`

### 收入
- [ ] `pricing_page_viewed`
- [ ] `checkout_started`
- [ ] `subscription_upgraded`
- [ ] `subscription_cancelled`

### 留存
- [ ] `session_started`
- [ ] `feature_[x]_used`(强力功能)

仪表板模板

项目类型 关键仪表板
SaaS 获取、激活、参与度、留存、收入
电子商务 转化漏斗、产品性能、客户 LTV
内容 消费、参与度、增长
AI/LLM 使用、质量、成本
移动应用 安装、引导、DAU/MAU、崩溃

始终包含的属性

// PostHog 自动丰富
$current_url
$browser
$device_type
$os

// 您自己添加这些
user_plan       // 'free' | 'pro' | 'enterprise'
user_role       // 'admin' | 'member'
company_id      // 对于 B2B
feature_context // 应用中的位置