diff --git a/README.md b/README.md
index c79ad14..e66e79d 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,7 @@ A lightweight web analytics SDK for tracking user visits and events.
- **Device Fingerprinting** - Canvas-based fingerprint for visitor identification
- **Offline Queue** - Local storage retry for failed requests
- **Auto Pageview** - Automatic page visit tracking
-- **Custom Events** - Track clicks, errors, and custom actions
+- **Custom Events** - Track any custom user actions via `track(eventName, data)`
- **Session Tracking** - Automatic session management (30min timeout)
## 📦 Install
@@ -53,8 +53,6 @@ init({
| `init(config)` | Initialize the tracker |
| `track(eventName, data)` | Track a custom event |
| `trackPageview(data)` | Track a page view |
-| `trackClick(selector, data)` | Auto-track clicks on element |
-| `trackError(data)` | Track JavaScript errors |
| `setUserId(userId)` | Set user ID after login |
| `getFingerprint()` | Get device fingerprint |
| `flush()` | Force send queued events |
@@ -87,6 +85,7 @@ WFTK.init({
"url": "https://example.com/page",
"title": "Page Title",
"referer": "https://google.com",
+ "referrer": "https://google.com",
"sessionId": "sess_xxx",
"sessionStart": 1699999000000,
"visitCount": 1,
@@ -96,11 +95,16 @@ WFTK.init({
"screen": "1920x1080",
"viewport": "1920x1080",
"language": "en-US"
+ },
+ "biz": {
+ "action": "cta_click"
}
}]
}
```
+`data` holds system-collected context (page, session, device), while `biz` holds the user-supplied payload passed to `track(eventName, data)`. They are kept separate to avoid overlap.
+
## 🖥️ Server Implementation
### Endpoint
diff --git a/package.json b/package.json
index c17b8a7..4895d20 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "@weavefox/tracker",
- "version": "0.1.2",
- "description": "Website Analytics SDK - Lightweight, secure tracking with anti-bot protection",
+ "version": "0.1.3",
+ "description": "Website Analytics SDK - Lightweight, secure tracking with anti-bot protection for vite coding applications.",
"main": "dist/tracer.umd.js",
"module": "dist/tracer.mjs",
"types": "dist/index.d.ts",
diff --git a/site/index.html b/site/index.html
index 900166f..af81e9b 100644
--- a/site/index.html
+++ b/site/index.html
@@ -329,7 +329,7 @@
特性
⚡
全自动采集
-
Pageview、点击、错误自动追踪
+
Pageview 自动追踪,自定义事件随心埋点
@@ -466,7 +466,7 @@ 会话统计
entry.innerHTML = `
${timeStr}
${event.event}
- ${event.data.action || `${event.data.browser?.name || ''} ${event.data.os || ''} ${event.data.deviceType || ''}`}
+ ${event.biz?.action || `${event.data.browser?.name || ''} ${event.data.os || ''} ${event.data.deviceType || ''}`}
`;
eventLog.insertBefore(entry, eventLog.firstChild);
diff --git a/src/index.ts b/src/index.ts
index 271aaca..1c6cb77 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -36,8 +36,8 @@ export function init(config: TrackerConfig): Tracer {
/**
* Track a custom event
*/
-export function track(eventName: string, data?: Record): void {
- trackerInstance?.track(eventName, data);
+export function track(event: string, data?: Record): void {
+ trackerInstance?.track(event, data);
}
/**
@@ -47,20 +47,6 @@ export function trackPageview(data?: Record): void {
trackerInstance?.trackPageview(data);
}
-/**
- * Track clicks on element (auto-bind)
- */
-export function trackClick(selector: string, data?: Record): void {
- trackerInstance?.trackClick(selector, data);
-}
-
-/**
- * Track JavaScript errors
- */
-export function trackError(data?: Record): void {
- trackerInstance?.trackError(data);
-}
-
/**
* Set user ID after login
*/
diff --git a/src/tracker.ts b/src/tracker.ts
index 0b4dfc1..1a75e45 100644
--- a/src/tracker.ts
+++ b/src/tracker.ts
@@ -19,8 +19,7 @@ import {
getViewport,
deepMerge,
getStorageKey,
- isSameSession,
- throttle
+ isSameSession
} from './utils';
import { sendData, sendQueue } from './sender';
@@ -36,22 +35,14 @@ export interface TrackerConfig {
enableBotFilter?: boolean;
}
-// 事件类型
-export type EventType =
- | 'pageview'
- | 'click'
- | 'scroll'
- | 'custom'
- | 'js_error'
- | 'performance';
-
// 事件数据
export interface TrackEvent {
- event: EventType;
+ event: string;
timestamp: number;
nonce: string;
fingerprint: string;
data: Record;
+ biz: Record;
userId?: string;
}
@@ -87,7 +78,6 @@ export class Tracer {
private userId?: string;
private eventCount = 0;
private isInitialized = false;
- private boundClickSelectors = new Set();
constructor(config: TrackerConfig) {
this.config = deepMerge(DEFAULT_CONFIG, config);
@@ -197,7 +187,7 @@ export class Tracer {
/**
* 构建事件数据
*/
- private buildEvent(eventType: EventType, data: Record = {}): TrackEvent {
+ private buildEvent(event: string, data: Record = {}): TrackEvent {
// 检查事件数量限制
this.eventCount++;
if (this.eventCount > (this.config.maxEventsPerSession || 1000)) {
@@ -206,14 +196,12 @@ export class Tracer {
}
const eventData: TrackEvent = {
- event: eventType,
+ event,
timestamp: getTimestamp(),
nonce: generateNonce(),
fingerprint: this.fingerprint,
- data: {
- ...this.getBaseData(),
- ...data
- },
+ data: this.getBaseData(),
+ biz: { ...data },
userId: this.userId
};
@@ -255,18 +243,18 @@ export class Tracer {
/**
* 上报事件
*/
- private async report(eventType: EventType, data: Record = {}): Promise {
+ private async report(event: string, data: Record = {}): Promise {
// 机器人过滤
if (this.config.enableBotFilter !== false && this.isBot) {
return;
}
try {
- const event = this.buildEvent(eventType, data);
+ const eventData = this.buildEvent(event, data);
const payload: Payload = {
appId: this.config.appId,
- events: [event]
+ events: [eventData]
};
// 发送到服务端
@@ -278,7 +266,7 @@ export class Tracer {
await sendData({ url, data: payload });
}
- this.log('Event tracked', { event: eventType, data });
+ this.log('Event tracked', { event, data });
} catch (error) {
if (this.config.debug) {
console.error('[WFTK] Track error:', error);
@@ -296,63 +284,8 @@ export class Tracer {
/**
* 追踪自定义事件
*/
- track(eventName: string, data: Record = {}): void {
- this.report('custom', { eventName, ...data });
- }
-
- /**
- * 追踪点击事件(自动绑定)
- */
- trackClick(selector: string, data: Record = {}): void {
- if (typeof document === 'undefined') return;
-
- // 防止重复绑定相同 selector
- if (this.boundClickSelectors.has(selector)) {
- this.warn(`Click handler for "${selector}" already bound`);
- return;
- }
- this.boundClickSelectors.add(selector);
-
- const handler = throttle((e: MouseEvent) => {
- const target = e.target as HTMLElement;
- const closest = target.closest(selector);
-
- if (closest) {
- this.report('click', {
- element: selector,
- tag: target.tagName.toLowerCase(),
- text: target.textContent?.slice(0, 50),
- ...data
- });
- }
- }, 1000);
-
- document.addEventListener('click', handler);
- }
-
- /**
- * 追踪 JavaScript 错误
- */
- trackError(data: Record = {}): void {
- if (typeof window === 'undefined') return;
-
- window.addEventListener('error', (event) => {
- this.report('js_error', {
- message: event.message,
- filename: event.filename,
- lineno: event.lineno,
- colno: event.colno,
- ...data
- });
- });
-
- window.addEventListener('unhandledrejection', (event) => {
- this.report('js_error', {
- message: event.reason?.message || 'Unhandled Promise Rejection',
- stack: event.reason?.stack,
- ...data
- });
- });
+ track(event: string, data: Record = {}): void {
+ this.report(event, data);
}
/**
diff --git a/src/utils.ts b/src/utils.ts
index 96b3c3d..3dbf29a 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -79,24 +79,6 @@ export function isSameSession(lastTime: number, timeout: number = 30 * 60 * 1000
return getTimestamp() - lastTime < timeout;
}
-/**
- * 节流函数
- */
-export function throttle any>(
- func: T,
- limit: number
-): (...args: Parameters) => void {
- let inThrottle = false;
-
- return function (this: any, ...args: Parameters) {
- if (!inThrottle) {
- func.apply(this, args);
- inThrottle = true;
- setTimeout(() => (inThrottle = false), limit);
- }
- };
-}
-
/**
* 获取存储键名前缀
*/