📊 Tracker - 事件与数据追踪系统
一个轻量级、类型安全的 React 事件与数据追踪框架,支持 DOM 事件自动追踪、React 状态监听和自定义追踪器。
✨ 特性
- 🎯 类型安全 - 完整的 TypeScript 类型支持
- 🔄 消息中心 - 统一的消息管理和分发机制
- 🖱️ 自动 DOM 追踪 - 自动监听 DOM 事件(点击、悬停等)
- 📦 状态追踪 - React useState 状态变更自动追踪
- 🎨 高度可定制 - 支持自定义追踪器和数据提取
- ⚡ 性能优化 - 防抖、节流、采样率控制
- 🪝 React Hooks - 开箱即用的 React Hooks
- 🔌 非侵入式 - 不影响原有业务逻辑
📦 安装
npm install light-web-track
# 或
yarn add light-web-track
# 或
pnpm add light-web-track
📖 使用
// 导出追踪器类
import { TrackerClass, EventTracker, DataTracker, UserTracker } from 'light-web-track';
// 导出消息中心
import { TrackerMessageCenter, messageCenter } from 'light-web-track';
// 导出追踪器类型
import { TrackerType } from 'light-web-track';
// 导出 DOM 事件追踪器
import { DOMEventTracker, ClickTracker, HoverTracker } from 'light-web-track';
// 导出 React 状态追踪
import {
ProxyDataTracker,
useTrackedState,
useTrackedObjectState,
createTrackedStateHook
} from 'light-web-track';
// 导出 React Hooks
import {
useDOMEventTracker,
useClickTracker,
useHoverTracker,
useCustomTracker
} from 'light-web-track';
🚀 快速开始
1. 初始化消息中心
在应用入口文件中注册消息处理器:
import { messageCenter, TrackerType } from 'light-web-track';
// 注册消息处理器
function setupTrackerHandlers() {
// 处理事件追踪消息(DOM 点击、悬停等)
messageCenter.on(TrackerType.Event, async (message) => {
console.log('Event:', message);
// 发送到分析平台,如 Google Analytics
// await sendToAnalytics(message);
});
// 处理数据追踪消息(状态变更等)
messageCenter.on(TrackerType.Data, async (message) => {
console.log('Data:', message);
// 发送到数据仓库
// await sendToDataWarehouse(message);
});
// 全局处理器(接收所有类型的消息)
messageCenter.onAll(async (message) => {
console.log('All:', message.type, message.timestamp);
// 统一日志记录
// await logToServer(message);
});
}
// 在应用启动时调用
setupTrackerHandlers();
2. DOM 事件追踪
使用 Hook(推荐)
// 方式 1: 使用简化的 useClickTracker
import { useClickTracker } from 'light-web-track';
function MyComponent() {
// 自动追踪所有按钮点击
useClickTracker();
return (
<div>
<button>点击我</button>
</div>
);
}
// 方式 2: 使用通用的 useDOMEventTracker
import { useDOMEventTracker } from 'light-web-track';
function MyComponent() {
useDOMEventTracker({
eventTypes: ['click'],
selector: 'button',
});
return (
<div>
<button>点击我</button>
</div>
);
}
使用追踪器类
import { ClickTracker, HoverTracker } from 'light-web-track';
// 全局点击追踪
const clickTracker = new ClickTracker({
selector: 'button, a, [data-track]',
samplingRate: 1.0, // 100% 采样
});
await clickTracker.start();
// 悬停追踪(带防抖)
const hoverTracker = new HoverTracker({
selector: '.card',
debounceDelay: 300, // 300ms 防抖
});
await hoverTracker.start();
3. React 状态追踪
import { useTrackedState } from 'light-web-track';
function Counter() {
// 自动追踪 count 状态变更
const [count, setCount] = useTrackedState(0, {
stateKey: 'count',
componentName: 'Counter',
});
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>+1</button>
</div>
);
}
4. 对象状态追踪
import { useTrackedObjectState } from 'light-web-track';
function UserForm() {
const [user, setUser] = useTrackedObjectState(
{ name: '', age: 0 },
{
stateKey: 'user',
componentName: 'UserForm',
trackFields: ['name', 'age'], // 只追踪这些字段
}
);
return (
<div>
<input
value={user.name}
onChange={(e) => setUser({ ...user, name: e.target.value })}
/>
<input
type="number"
value={user.age}
onChange={(e) => setUser({ ...user, age: Number(e.target.value) })}
/>
</div>
);
}
🏗️ 核心概念
系统架构
Tracker 采用生产者-消费者架构:
┌─────────────────────────────────────────────────────────────┐
│ 追踪器 (Producer) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ 1. beforeTracking() - 条件判断、数据验证 │ │
│ │ 2. trackingHandler() - 发送消息到消息中心 │ │
│ │ 3. afterTracking() - 清理、统计、通知 │ │
│ │ 4. onTrackingError() - 错误处理 │ │
│ └──────────────────────────────────────────────────────┘ │
└────────────────────┬────────────────────────────────────────┘
│ 发送消息 (TrackerMessage)
┌────────────────────▼────────────────────────────────────────┐
│ 消息中心 (TrackerMessageCenter) │
│ 负责消息的分发到注册的处理器 │
└────────────────────┬────────────────────────────────────────┘
│ 分发消息
┌────────────────────▼────────────────────────────────────────┐
│ 消息处理器 (Consumer) │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 事件处理器 │ │ 数据处理器 │ │
│ │ 上报到分析平台 │ │ 存储到数据库 │ │
│ └──────────────────┘ └──────────────────┘ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 性能监控器 │ │ 错误监控器 │ │
│ │ 分析性能指标 │ │ 收集错误信息 │ │
│ └──────────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────────────┘
TrackerClass 生命周期
每个追踪器的执行流程:
┌────────────────────────────────────────┐
│ tracker.run(runtimePayload?) │
└────────────────┬───────────────────────┘
│
▼
┌────────────────────┐
│ beforeTracking() │ ◄─── 返回 false 则停止
│ 条件判断/验证 │
└────────┬───────────┘
│
▼
┌────────────────────┐
│ trackingHandler() │ ◄─── 发送消息
│ 核心业务逻辑 │
└────────┬───────────┘
│
┌────────▼────────┐
│ 成功 or 异常 │
└────────┬─────┬──┘
│ │
(成功) │ │ (异常)
▼ ▼
┌──────────────┐ ┌──────────────────┐
│afterTracking │ │ onTrackingError()│
│清理/统计/通知│ │ 错误处理 │
└──────────────┘ └──────────────────┘
消息中心 (TrackerMessageCenter)
消息中心是整个追踪系统的核心,负责消息的发送、接收和分发。
import { messageCenter, TrackerType } from 'light-web-track';
// 发送消息
await messageCenter.send(TrackerType.Event, {
eventType: 'click',
target: 'button',
});
// 注册处理器
const handler = async (message) => {
console.log(message);
};
messageCenter.on(TrackerType.Event, handler);
// 移除处理器
messageCenter.off(TrackerType.Event, handler);
// 清空所有处理器
messageCenter.clear();
追踪器基类 (TrackerClass)
所有追踪器都继承自 TrackerClass,提供了统一的生命周期管理。
import { TrackerClass } from 'light-web-track';
class MyTracker extends TrackerClass<MyPayload> {
// 前置钩子:决定是否执行追踪
protected async beforeTracking(payload: MyPayload): Promise<boolean> {
// 自定义条件判断
return payload.value > 0;
}
// 核心钩子:执行追踪逻辑
protected async trackingHandler(payload: MyPayload): Promise<void> {
await this.sendMessage(payload);
}
// 后置钩子:追踪完成后的操作
protected async afterTracking(payload: MyPayload): Promise<void> {
console.log('Tracked:', payload);
}
// 错误处理
protected async onTrackingError(error: Error, payload: MyPayload): Promise<void> {
console.error('Error:', error);
}
}
追踪器类型
enum TrackerType {
Basic = 'Base', // 基础追踪
Event = 'Web_Event', // DOM 事件追踪
Data = 'Data', // 数据变更追踪
User = 'User', // 用户行为追踪
}
📚 API 文档
DOMEventTracker
自动监听 DOM 事件的追踪器。
interface DOMEventTrackerConfig {
eventTypes: string[]; // 事件类型数组,如 ['click', 'mouseenter']
selector?: string; // CSS 选择器(默认 '*')
samplingRate?: number; // 采样率 0-1(默认 1.0)
debounceDelay?: number; // 防抖延迟(毫秒,默认 0)
throttleDelay?: number; // 节流延迟(毫秒,默认 0)
extractData?: (e: Event, el: HTMLElement) => any; // 自定义数据提取
disableAutoTrack?: boolean; // 禁用自动追踪(默认 false)
}
方法:
start()- 开始追踪stop()- 停止追踪track(event, target)- 手动追踪单个事件
内置追踪器:
ClickTracker- 点击事件追踪器(eventTypes: ['click'])HoverTracker- 悬停事件追踪器(eventTypes: ['mouseenter'])
ProxyDataTracker
React 状态追踪器,用于监听 useState 的状态变更。
interface UseStateTrackingData {
stateKey: string; // 状态键名
oldValue: any; // 旧值
newValue: any; // 新值
componentName?: string; // 组件名称
timestamp?: number; // 时间戳
}
React Hooks
useDOMEventTracker
通用的 DOM 事件追踪 Hook。
function useDOMEventTracker(
config: DOMEventTrackerConfig,
deps?: DependencyList
): DOMEventTracker | null;
useClickTracker
简化的点击追踪 Hook,预配置了点击事件。
function useClickTracker(
selector?: string, // CSS 选择器(默认 'button, a, [data-track-click]')
samplingRate?: number // 采样率 0-1(默认 1.0)
): ClickTracker | null;
// 使用示例
useClickTracker(); // 使用默认配置
useClickTracker('.custom-button'); // 自定义选择器
useClickTracker('button', 0.5); // 50% 采样率
useHoverTracker
简化的悬停追踪 Hook,预配置了鼠标悬停事件。
function useHoverTracker(
selector?: string, // CSS 选择器(默认 '[data-track-hover]')
throttleDelay?: number // 节流延迟(默认 500ms)
): HoverTracker | null;
// 使用示例
useHoverTracker(); // 使用默认配置(500ms 节流)
useHoverTracker('.card'); // 自定义选择器
useHoverTracker('.card', 1000); // 1 秒节流
useCustomTracker
更灵活的自定义追踪 Hook,支持完整的配置选项。
function useCustomTracker(
options: DOMEventTrackerConfig
): DOMEventTracker | null;
// 使用示例
useCustomTracker({
eventTypes: ['click', 'dblclick', 'contextmenu'],
selector: '.interactive-element',
samplingRate: 0.8,
extractData: (event, element) => ({
elementId: element.id,
timestamp: Date.now(),
}),
});
useTrackedState
function useTrackedState<T>(
initialValue: T,
options: TrackingOptions
): [T, React.Dispatch<React.SetStateAction<T>>];
interface TrackingOptions {
stateKey: string;
componentName?: string;
disableTracking?: boolean;
}
useTrackedObjectState
function useTrackedObjectState<T extends Record<string, any>>(
initialValue: T,
options: ObjectTrackingOptions
): [T, React.Dispatch<React.SetStateAction<T>>];
interface ObjectTrackingOptions extends TrackingOptions {
trackFields?: (keyof T)[]; // 指定要追踪的字段
}
createTrackedStateHook
创建自定义的状态追踪 Hook。
function createTrackedStateHook<T>(
defaultOptions: Partial<TrackingOptions>
): (
initialValue: T,
options?: Partial<TrackingOptions>
) => [T, React.Dispatch<React.SetStateAction<T>>];
🎯 使用场景
1. 用户行为分析
// 追踪所有交互元素
useDOMEventTracker({
eventTypes: ['click', 'mouseenter'],
selector: 'button, a, [role="button"]',
});
2. 表单输入监控
const [formData, setFormData] = useTrackedObjectState(
{ email: '', password: '' },
{
stateKey: 'loginForm',
componentName: 'LoginPage',
}
);
3. 性能监控
class PerformanceTracker extends TrackerClass<PerformanceData> {
constructor() {
super(TrackerType.Basic, {} as PerformanceData);
}
async measurePageLoad() {
const navigationTiming = performance.getEntriesByType('navigation')[0];
await this.run({
loadTime: navigationTiming.duration,
domContentLoaded: navigationTiming.domContentLoadedEventEnd,
// ... 更多性能指标
});
}
}
4. 自定义事件追踪
class CustomTracker extends EventTracker {
async trackCustomEvent(eventName: string, data: any) {
await this.run({
eventType: 'custom',
eventName,
data,
timestamp: Date.now(),
});
}
}
⚡ 性能优化
防抖 (Debounce)
延迟执行,适用于搜索输入等高频事件。
useDOMEventTracker({
eventTypes: ['input'],
selector: 'input[type="search"]',
debounceDelay: 500, // 500ms 后执行
});
节流 (Throttle)
限制执行频率,适用于滚动、鼠标移动等。
useDOMEventTracker({
eventTypes: ['mousemove'],
selector: '.interactive-canvas',
throttleDelay: 100, // 每 100ms 最多执行一次
});
采样率控制
减少追踪数据量,适用于高流量场景。
useDOMEventTracker({
eventTypes: ['click'],
selector: '*',
samplingRate: 0.1, // 只追踪 10% 的事件
});
🔧 扩展指南
扩展追踪器类型
创建追踪器很简单,只需继承 TrackerClass 并实现三个生命周期钩子。
步骤 1:定义 Payload 类型
// 在 typing.d.ts 中添加
declare namespace Tracker {
interface CustomTrackerPayload {
action: string;
value: number;
metadata?: Record<string, any>;
}
}
步骤 2:创建追踪器类
// 创建文件:CustomTracker.ts
import { TrackerClass } from './TrackerClass';
import { TrackerType } from './constant';
class CustomTracker extends TrackerClass<Tracker.CustomTrackerPayload> {
constructor() {
super(TrackerType.Basic, { action: '', value: 0 });
}
/**
* 前置钩子:决定什么时候触发
* 用于条件判断、数据验证、采样控制等
*/
protected async beforeTracking(payload: Tracker.CustomTrackerPayload): Promise<boolean> {
// 示例:只追踪 value > 0 的情况
if (payload.value <= 0) {
console.log('[CustomTracker] value 不符合条件,跳过追踪');
return false;
}
// 示例:采样控制
if (Math.random() > 0.8) {
// 只追踪 80% 的事件
return false;
}
return true;
}
/**
* 核心钩子:决定触发时做什么
* 通常是发送消息到消息中心,并可进行数据转换
*/
protected async trackingHandler(payload: Tracker.CustomTrackerPayload): Promise<void> {
// 数据转换或增强
const enrichedData = {
...payload,
timestamp: Date.now(),
userAgent: navigator.userAgent,
};
// 发送到消息中心
await this.sendMessage(enrichedData, { source: 'custom-tracker' });
}
/**
* 后置钩子:决定触发后做什么
* 用于清理、统计、日志等
*/
protected async afterTracking(payload: Tracker.CustomTrackerPayload): Promise<void> {
console.log('[CustomTracker] 追踪完成:', payload.action);
// 可以在这里更新本地统计、触发后续流程等
}
// 可选:覆盖错误处理
protected async onTrackingError(
error: Error,
payload: Tracker.CustomTrackerPayload
): Promise<void> {
console.error('[CustomTracker] 追踪失败:', {
action: payload.action,
error: error.message,
});
// 可以上报到错误监控系统
}
}
export { CustomTracker };
步骤 3:使用追踪器
// 方式 1:使用静态 payload(构造时确定)
const tracker = new CustomTracker();
await tracker.run({ action: 'search', value: 100 });
// 方式 2:使用动态 payload(运行时传入)
const tracker = new CustomTracker();
const userAction = getUserAction();
await tracker.run(userAction);
新增追踪器类型
如果需要系统中新的追踪器类型,有两种方式:
方式 1:新增枚举值(推荐)
// 在 constant.ts 中
export enum TrackerType {
Basic = 'Base',
Event = 'Web_Event',
Data = 'Data',
User = 'User',
Custom = 'Custom', // 新增
Performance = 'Performance' // 新增
}
然后创建对应的追踪器类:
class CustomTracker extends TrackerClass<CustomPayload> {
constructor(payload: CustomPayload) {
super(TrackerType.Custom, payload);
}
// ... 实现生命周期钩子
}
class PerformanceTracker extends TrackerClass<PerformancePayload> {
constructor(payload: PerformancePayload) {
super(TrackerType.Performance, payload);
}
// ... 实现生命周期钩子
}
方式 2:使用现有类型扩展
如果不想新增枚举值,可以用现有类型:
class CustomTracker extends EventTracker<CustomPayload> {
constructor(payload: CustomPayload) {
super(payload, TrackerType.Event); // 复用 Event 类型
}
// ... 自定义实现
}
创建 React Hook 追踪器
如果需要在 React 组件中使用自定义追踪器,可以创建 Hook:
// 在 useTracker.ts 中添加
import { useEffect } from 'react';
import { CustomTracker } from './CustomTracker';
function useCustomTracker(action: string, value: number) {
useEffect(() => {
const tracker = new CustomTracker();
// 异步执行追踪,不阻塞渲染
tracker.run({ action, value }).catch(err => {
console.error('Tracker error:', err);
});
// 清理(虽然追踪器不需要清理,但这是好的实践)
return () => {
// 可选:如果追踪器有清理逻辑,在这里调用
};
}, [action, value]);
}
export { useCustomTracker };
注册消息处理器
新增的追踪器类型需要在初始化时注册对应的消息处理器:
// 在应用入口(通常是 main.tsx)
import { messageCenter, TrackerType } from 'light-web-track';
import { setupTrackerHandlers } from 'light-web-track';
function setupCustomTrackerHandlers() {
// 处理自定义追踪消息
messageCenter.on(TrackerType.Custom, async (message) => {
console.log('Custom tracker message:', message);
// 发送到后端或分析平台
// await sendToBackend(message);
});
// 处理性能追踪消息
messageCenter.on(TrackerType.Performance, async (message) => {
console.log('Performance tracker message:', message);
// 发送到性能监控系统
// await sendToMonitoringSystem(message);
});
}
// 应用启动时调用
setupTrackerHandlers();
setupCustomTrackerHandlers();
完整扩展示例:用户行为追踪器
以下是一个完整的扩展示例,展示如何创建用户行为追踪器:
// 定义类型
declare namespace Tracker {
interface UserActionPayload {
userId: string;
action: 'login' | 'logout' | 'signup' | 'purchase';
details?: Record<string, any>;
}
}
// 创建追踪器
class UserActionTracker extends TrackerClass<Tracker.UserActionPayload> {
constructor() {
super(TrackerType.User, { userId: '', action: 'login' });
}
protected async beforeTracking(payload: Tracker.UserActionPayload): Promise<boolean> {
// 检查用户 ID 是否存在
if (!payload.userId) {
console.warn('[UserActionTracker] 用户 ID 为空,跳过追踪');
return false;
}
// 检查用户是否同意数据收集
const hasConsent = await this.checkUserConsent(payload.userId);
return hasConsent;
}
protected async trackingHandler(payload: Tracker.UserActionPayload): Promise<void> {
const enrichedData = {
...payload,
timestamp: Date.now(),
sessionId: this.getSessionId(),
source: 'user-action-tracker',
};
await this.sendMessage(enrichedData);
}
protected async afterTracking(payload: Tracker.UserActionPayload): Promise<void> {
// 记录追踪成功
console.log(`[UserActionTracker] 用户 ${payload.userId} 的 ${payload.action} 已追踪`);
}
private async checkUserConsent(userId: string): Promise<boolean> {
// 实现用户同意逻辑
return true; // 简化示例
}
private getSessionId(): string {
// 获取或生成 Session ID
return sessionStorage.getItem('sessionId') || this.generateSessionId();
}
private generateSessionId(): string {
const sessionId = Math.random().toString(36).substring(7);
sessionStorage.setItem('sessionId', sessionId);
return sessionId;
}
}
// 创建对应的 React Hook
function useUserActionTracker(userId: string) {
return {
trackAction: async (action: 'login' | 'logout' | 'signup' | 'purchase', details?: any) => {
const tracker = new UserActionTracker();
await tracker.run({ userId, action, details });
},
};
}
// 在组件中使用
function LoginPage() {
const { trackAction } = useUserActionTracker('user123');
const handleLogin = async () => {
// 登录逻辑...
await trackAction('login', { method: 'password' });
};
return <button onClick={handleLogin}>登录</button>;
}
// 注册消息处理器
messageCenter.on(TrackerType.User, async (message) => {
const payload = message.payload as Tracker.UserActionPayload;
// 发送到后端
await fetch('/api/track/user-action', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
});
扩展清单
创建新的追踪器时,请确保完成以下步骤:
- 在
typing.d.ts中定义 Payload 类型 - 创建追踪器类文件(或在现有文件中添加)
- 实现三个生命周期钩子:
beforeTracking、trackingHandler、afterTracking - 可选:实现
onTrackingError错误处理 - 如果需要新的追踪器类型,在
constant.ts中添加枚举值 - 在
index.ts中导出新的追踪器类 - 如果需要 React Hook,在
useTracker.ts中创建相应 Hook - 在应用初始化时注册消息处理器(消息消费端)
- 为新的追踪器类型添加单元测试(可选但推荐)
- 更新此 README 文档,说明新追踪器的使用方式
🔧 高级配置
自定义数据提取
useDOMEventTracker({
eventTypes: ['click'],
selector: 'button',
extractData: (event, element) => {
return {
buttonText: element.textContent,
buttonId: element.id,
userAgent: navigator.userAgent,
screenWidth: window.innerWidth,
// ... 任何你需要的数据
};
},
});
手动追踪
const tracker = useDOMEventTracker({
eventTypes: ['click'],
selector: 'button',
disableAutoTrack: true, // 禁用自动追踪
});
const handleClick = (e: MouseEvent) => {
if (shouldTrack(e)) {
tracker?.track(e, e.target as HTMLElement);
}
};
条件追踪
class ConditionalTracker extends EventTracker {
protected async beforeTracking(payload: any): Promise<boolean> {
// 只追踪登录用户
const isLoggedIn = await checkUserLogin();
if (!isLoggedIn) {
return false;
}
// 采样控制
if (Math.random() > 0.5) {
return false;
}
return true;
}
}
⚠️ 常见问题与注意事项
Hook 导出问题
当前所有 Hooks 已在 index.ts 中导出,可以直接导入:
// ✅ 正确的导入方式
import { useClickTracker } from 'light-web-track';
追踪器类型值
实际的 TrackerType 枚举值:
TrackerType.Basic = 'Base'
TrackerType.Event = 'Web_Event'
TrackerType.Data = 'Data'
TrackerType.User = 'User'
生命周期钩子返回值
// beforeTracking() 返回值说明
return false; // 跳过追踪
return true; // 继续执行
return undefined; // 等同于 true,继续执行
return Promise<...> // 支持异步,结果转为布尔值
// trackingHandler() 返回值说明
// 无需返回值,但支持异步执行
// afterTracking() 返回值说明
// 无需返回值,但支持异步执行
// onTrackingError() 返回值说明
// 无需返回值,但支持异步执行
性能考虑
- 高频事件(如
mousemove、scroll)务必使用节流或防抖 - 在生产环境中根据流量使用采样率
- 追踪数据应异步发送,不要阻塞 UI
- 追踪器会自动捕获错误,不会影响业务逻辑
- 避免在
trackingHandler()中进行 DOM 操作,会影响性能
内存管理
- 组件卸载时追踪器会自动清理
- 使用 Hook 方式会自动管理生命周期
- 手动创建的追踪器需要手动调用
stop() - 不要创建循环引用,避免内存泄漏
数据安全
- 不要在追踪消息中包含敏感信息(密码、密钥、个人隐私)
- 始终检查用户是否同意数据收集
- 遵守 GDPR、CCPA 等数据保护法规
- 在
beforeTracking()中实现用户同意检查
protected async beforeTracking(payload: TPayload): Promise<boolean> {
// 检查用户是否同意收集数据
const hasConsent = await checkUserConsent();
if (!hasConsent) {
return false; // 不追踪
}
return true;
}
消息处理器的异常处理
消息处理器中的异常不会影响追踪器本身,但应该妥善处理:
messageCenter.on(TrackerType.Event, async (message) => {
try {
await sendToAnalyticsServer(message);
} catch (error) {
// 上报到错误监控系统,但不影响应用运行
console.error('Failed to send tracking data:', error);
await reportErrorToMonitoring(error);
}
});
扩展时常见错误
1. 忘记调用 sendMessage()
// ❌ 错误:没有发送消息
protected async trackingHandler(payload: TPayload): Promise<void> {
// 只做了数据处理,没有发送消息
const enriched = { ...payload, timestamp: Date.now() };
// 消息不会被消息中心处理
}
// ✅ 正确
protected async trackingHandler(payload: TPayload): Promise<void> {
const enriched = { ...payload, timestamp: Date.now() };
await this.sendMessage(enriched); // 必须调用
}
2. 在 beforeTracking() 中进行副作用操作
// ❌ 错误:beforeTracking 返回 false,但副作用已经发生
protected async beforeTracking(payload: TPayload): Promise<boolean> {
await updateLocalStorage(payload); // 如果返回 false,这会是浪费操作
return shouldTrack(payload);
}
// ✅ 正确:只进行条件判断
protected async beforeTracking(payload: TPayload): Promise<boolean> {
// 只判断条件,不做其他操作
return shouldTrack(payload);
}
// 副作用放在 afterTracking()
protected async afterTracking(payload: TPayload): Promise<void> {
await updateLocalStorage(payload); // 只有追踪成功才会执行
}
3. 没有处理异步操作中的错误
// ❌ 不好的做法:async/await 中可能抛出异常
protected async trackingHandler(payload: TPayload): Promise<void> {
const response = await fetch(SERVER, { /* ... */ });
const data = await response.json(); // 可能会抛出异常
await this.sendMessage(data);
}
// ✅ 最佳做法:异常已由 run() 方法处理
// 不需要额外处理,run() 会捕获所有异常并调用 onTrackingError()
protected async trackingHandler(payload: TPayload): Promise<void> {
const response = await fetch(SERVER, { /* ... */ });
const data = await response.json();
await this.sendMessage(data);
// 如果有异常,会被 run() 方法捕获
}
4. 在 React Hook 中忘记清理
// ⚠️ 组件多次渲染时会重复创建追踪器
function useMyCustomTracker() {
const handleClick = async () => {
const tracker = new MyTracker();
await tracker.run(data);
};
return { handleClick };
}
// ✅ 正确做法:缓存追踪器实例
function useMyCustomTracker() {
const trackerRef = useRef<MyTracker | null>(null);
if (!trackerRef.current) {
trackerRef.current = new MyTracker();
}
const handleClick = async (data: TPayload) => {
await trackerRef.current!.run(data);
};
return { handleClick };
}
调试技巧
1. 启用详细日志
所有追踪器都会在控制台输出日志:
// 追踪器会输出这样的日志:
// [Tracker] beforeTracking 返回 false,跳过追踪
// [EventTracker] 准备追踪事件: {...}
// [EventTracker] 事件追踪完成
2. 监听所有消息
messageCenter.onAll((message) => {
console.log('All tracking messages:', message);
});
调试技巧
1. 启用详细日志
所有追踪器都会在控制台输出日志:
// 追踪器会输出这样的日志:
// [Tracker] beforeTracking 返回 false,跳过追踪
// [EventTracker] 准备追踪事件: {...}
// [EventTracker] 事件追踪完成
2. 监听所有消息
messageCenter.onAll((message) => {
console.log('All tracking messages:', message);
});
3. 测试 beforeTracking() 条件
const tracker = new MyTracker();
// 直接调用钩子来测试条件
const shouldTrack = await tracker['beforeTracking']({ /* payload */ });
console.log('Should track:', shouldTrack);
📁 项目文件结构说明
src/
├── index.ts # 统一导出入口
├── core/ # 核心模块
│ ├── constant.ts # 追踪器类型和常量定义
│ ├── TrackerClass.ts # 追踪器基类和实现类
│ └── TrackerMessageCenter.ts # 消息中心实现
├── trackers/ # 追踪器实现
│ ├── DOMEventTracker.ts # DOM 事件追踪器
│ └── ProxyDataTracker.ts # React 状态追踪器
├── hooks/ # React Hooks
│ └── useTracker.ts # 追踪器 Hooks
├── types/ # 类型定义
│ └── typing.d.ts # 全局类型定义
└── README.md # 文档
文件职责
| 文件 | 职责 |
|---|---|
core/constant.ts | 定义追踪器类型枚举和业务常量 |
core/TrackerClass.ts | 实现追踪器基类和生命周期管理 |
core/TrackerMessageCenter.ts | 实现消息中心和分发机制 |
trackers/DOMEventTracker.ts | 实现 DOM 事件追踪器 |
trackers/ProxyDataTracker.ts | 实现 React 状态追踪器 |
hooks/useTracker.ts | 实现 React Hooks(生产者端) |
types/typing.d.ts | 全局类型定义(Tracker 命名空间) |
index.ts | 统一导出公共 API |
添加新追踪器的文件修改清单
如果需要添加新的追踪器,需要修改以下文件:
1. constant.ts(可选)
如果需要新的追踪器类型,添加枚举值:
export enum TrackerType {
// 现有类型...
MyCustom = 'My_Custom' // ← 添加新类型
}
2. typing.d.ts
添加新 Payload 类型定义:
declare namespace Tracker {
interface MyCustomPayload {
// 你的属性定义
}
}
3. 新建文件或在现有文件添加追踪器类
创建追踪器类或扩展现有类:
export class MyCustomTracker extends TrackerClass<Tracker.MyCustomPayload> {
// 实现...
}
4. useTracker.ts(可选)
如果需要 React Hook,添加对应的 Hook:
export function useMyCustomTracker(options?: any) {
// 实现...
}
5. index.ts
导出新的追踪器和 Hook:
export { MyCustomTracker } from './YourFile';
export { useMyCustomTracker } from './useTracker';
6. 应用初始化
在应用启动时注册消息处理器:
messageCenter.on(TrackerType.MyCustom, async (message) => {
// 处理消息
});
✅ 扩展检查表
创建新追踪器时,请按以下步骤检查:
设计阶段
- 确定追踪器名称和类型
- 设计 Payload 数据结构
- 决定是否需要新的追踪器枚举类型
- 列出生命周期钩子需要执行的操作
开发阶段
- 在
typing.d.ts中定义 Payload 类型 - 在
constant.ts中添加追踪器类型(如需要) - 创建追踪器类,实现四个钩子方法
- 编写
beforeTracking()条件判断逻辑 - 编写
trackingHandler()消息发送逻辑 - 编写
afterTracking()清理逻辑 - 覆盖
onTrackingError()错误处理(如需要)
集成阶段
- 在
index.ts中导出追踪器类 - 创建对应的 React Hook(如需要)
- 在
useTracker.ts中导出 Hook - 在
index.ts中导出 Hook - 在应用初始化时注册消息处理器
- 实现消息处理器的具体逻辑
测试阶段
- 测试
beforeTracking()的条件判断 - 测试
trackingHandler()发送的消息内容 - 测试
afterTracking()的清理逻辑 - 测试错误情况下
onTrackingError()的处理 - 测试消息处理器是否正确接收消息
- 测试 React Hook 在组件挂载/卸载时的生命周期
- 测试高频调用时的性能表现
- 测试内存泄漏(长期运行)
文档和维护
- 更新 README 中的使用示例
- 添加代码注释和文档字符串
- 编写追踪器的使用说明
- 记录 Payload 中的各个字段含义
- 记录消息处理器的处理逻辑
🎯 最佳实践
1. Payload 设计原则
// ✅ 好的 Payload 设计
interface MyPayload {
// 必须字段:标识性信息
actionId: string;
// 核心字段:业务关键数据
userId: string;
action: string;
// 可选字段:额外上下文
metadata?: Record<string, any>;
// 避免冗余:不要重复存储系统时间戳
// 系统会自动添加到消息中的 timestamp 字段
}
2. Hook 设计原则
// ✅ 好的 Hook 设计
function useMyTracker(dependencies: any[]) {
const trackerRef = useRef<MyTracker | null>(null);
if (!trackerRef.current) {
trackerRef.current = new MyTracker();
}
useEffect(() => {
// Hook 只负责追踪器的创建和销毁
// 具体的追踪逻辑由使用者决定
return () => {
// 可选:清理逻辑
};
}, dependencies);
return trackerRef.current;
}
3. 消息处理器设计原则
// ✅ 好的消息处理器设计
messageCenter.on(TrackerType.MyType, async (message) => {
try {
// 1. 验证消息内容
const payload = message.payload as Tracker.MyPayload;
if (!payload.actionId) {
console.warn('Invalid payload: missing actionId');
return;
}
// 2. 异步处理(不阻塞)
await sendToBackend(payload);
// 3. 错误处理(不影响应用)
} catch (error) {
console.error('Failed to process message:', error);
await reportToMonitoring(error);
}
});
4. 避免常见陷阱
// ❌ 陷阱 1:在 beforeTracking 中进行 DOM 操作
protected async beforeTracking(payload: TPayload): Promise<boolean> {
document.querySelector('.loading').style.display = 'none'; // ❌ 不要
return true;
}
// ✅ 正确:只在 afterTracking 中
protected async afterTracking(payload: TPayload): Promise<void> {
// 如果需要 DOM 操作,使用 requestAnimationFrame
requestAnimationFrame(() => {
document.querySelector('.loading').style.display = 'none';
});
}
// ❌ 陷阱 2:消息处理器中未捕获异常
messageCenter.on(TrackerType.MyType, async (message) => {
await fetch(SERVER); // ❌ 如果失败,可能导致应用崩溃
});
// ✅ 正确:总是捕获异常
messageCenter.on(TrackerType.MyType, async (message) => {
try {
await fetch(SERVER);
} catch (error) {
// 记录但不抛出
console.error('Failed:', error);
}
});
// ❌ 陷阱 3:同步阻塞操作
protected async trackingHandler(payload: TPayload): Promise<void> {
const data = heavyComputationSync(payload); // ❌ 阻塞 UI
await this.sendMessage(data);
}
// ✅ 正确:使用 Web Worker 或异步处理
protected async trackingHandler(payload: TPayload): Promise<void> {
const data = await heavyComputationAsync(payload); // 异步处理
await this.sendMessage(data);
}