onerway-analytics 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # onerway-analytics
2
2
 
3
- 客户端埋点 SDK,支持双发:自建数据服务 + Google Analytics 4。
3
+ 客户端埋点 SDK,支持双发:自建数据服务 + Google Tag Manager(GTM)。
4
4
 
5
5
  ## 安装
6
6
 
@@ -14,6 +14,7 @@ npm install onerway-analytics
14
14
 
15
15
  ```html
16
16
  <script src="./dist/bundle/index.js"></script>
17
+ <!-- 全局变量:window.OnrwayAnalytics -->
17
18
  ```
18
19
 
19
20
  ---
@@ -24,66 +25,58 @@ npm install onerway-analytics
24
25
  import { createAnalytics } from 'onerway-analytics';
25
26
 
26
27
  const analytics = createAnalytics({
27
- appKey: 'your-app-key', // 应用标识
28
- serverUrl: 'https://your-server.com/collect', // 数据接收端点
29
- env: 'prod', // 环境: beta | test | uat | prod
30
- debug: true, // 开启控制台日志
28
+ appKey: 'your-app-key',
29
+ serverUrl: 'https://your-server.com/collect',
30
+ env: 'prod',
31
+ debug: true,
31
32
  });
32
33
  ```
33
34
 
34
35
  ### 上报模式
35
36
 
36
- SDK 支持三种上报模式,按需选择。
37
-
38
- #### 双发(自建 + GA)
39
-
40
- 同时发送到自建服务和 Google Analytics 4:
37
+ #### 双发(自建服务 + GTM)
41
38
 
42
39
  ```ts
43
40
  const analytics = createAnalytics({
44
- appKey: 'your-app-key',
41
+ appKey: 'your-app-key',
45
42
  serverUrl: 'https://your-server.com/collect',
46
- env: 'prod',
47
- ga: {
48
- measurementId: 'G-XXXXXXXXXX',
49
- enabled: true,
50
- sendPageView: false,
51
- debugMode: true,
43
+ env: 'prod',
44
+ gtm: {
45
+ containerId: 'GTM-XXXXXXX',
46
+ enabled: true,
52
47
  },
53
48
  });
54
49
  ```
55
50
 
56
51
  #### 只发自建服务
57
52
 
58
- 不配置 `ga`,或设置 `ga.enabled: false`:
53
+ 不配置 `gtm`,或设置 `gtm.enabled: false`:
59
54
 
60
55
  ```ts
61
56
  const analytics = createAnalytics({
62
- appKey: 'your-app-key',
57
+ appKey: 'your-app-key',
63
58
  serverUrl: 'https://your-server.com/collect',
64
- env: 'prod',
65
- debug: true,
59
+ env: 'prod',
66
60
  });
67
61
  ```
68
62
 
69
- #### 只发 GA
63
+ #### 只发 GTM
70
64
 
71
- 设置 `disableServer: true`,`serverUrl` 可留空。page_view、自动埋点、错误监控、性能指标均只走 GA
65
+ 设置 `disableServer: true`,`serverUrl` 可留空,所有事件只推入 dataLayer
72
66
 
73
67
  ```ts
74
68
  const analytics = createAnalytics({
75
- appKey: 'your-app-key',
76
- serverUrl: '',
69
+ appKey: 'your-app-key',
70
+ serverUrl: '',
77
71
  disableServer: true,
78
- ga: {
79
- measurementId: 'G-XXXXXXXXXX',
80
- enabled: true,
81
- debugMode: true,
72
+ gtm: {
73
+ containerId: 'GTM-XXXXXXX',
74
+ enabled: true,
82
75
  },
83
76
  });
84
77
  ```
85
78
 
86
- > `disableServer` 开启后 `analytics.flush()` 和自动缓冲机制均不生效。
79
+ > `disableServer: true` 时,`analytics.flush()` 和重试机制均不生效。
87
80
 
88
81
  ---
89
82
 
@@ -93,68 +86,58 @@ const analytics = createAnalytics({
93
86
 
94
87
  ```ts
95
88
  // 普通事件(进入缓冲区,定时批量发送)
96
- analytics.track('purchase', { product_id: '123', amount: 99.9 });
89
+ analytics.track('button_click', { target_id: 'buy_btn', scene: 'product' });
97
90
 
98
- // 关键事件(立即发送,不等待缓冲区)
91
+ // 关键事件(立即发送,不等缓冲区)
99
92
  analytics.track('payment_success', { order_id: '456' }, { keyEvent: true });
100
93
  ```
101
94
 
102
95
  ### 用户标识
103
96
 
104
97
  ```ts
105
- // 设置用户 ID(同步到 GA 的 user_id)
106
- analytics.identify('user-123', { vip: true, level: 'gold' });
98
+ // 设置用户 ID 与属性
99
+ analytics.identify('user-123', { plan: 'pro', email: 'user@example.com' });
107
100
 
108
101
  // 获取用户信息
109
- analytics.getUserProfile();
110
- // { userId: 'user-123', anonymousId: 'uuid...', attributes: { vip: true } }
102
+ const profile = analytics.getUserProfile();
103
+ // { userId: 'user-123', anonymousId: 'uuid...', attributes: { plan: 'pro' } }
104
+
105
+ // 重置用户身份
106
+ analytics.reset();
111
107
  ```
112
108
 
113
- ### GA 编程式控制
109
+ ### GTM 编程式控制
114
110
 
115
111
  ```ts
116
- // 直发 GA 事件(不走 SDK 服务端)
117
- analytics.ga.track('login', { method: 'email' });
112
+ // 直接推送自定义数据到 dataLayer
113
+ analytics.gtm.push({ event: 'custom_event', key: 'value' });
118
114
 
119
- // 设置 GA 用户属性
120
- analytics.ga.setUserProperties({ plan: 'premium' });
115
+ // 推送带事件名的数据
116
+ analytics.gtm.track('checkout_start', { step: 1 });
121
117
 
122
- // GDPR 同意模式
123
- analytics.ga.consent('default', {
124
- analytics_storage: 'denied',
125
- ad_storage: 'denied',
126
- });
127
- analytics.ga.consent('update', {
128
- analytics_storage: 'granted',
129
- });
118
+ // 设置用户 ID
119
+ analytics.gtm.setUserId('user-123');
130
120
 
131
- // GA4 配置
132
- analytics.ga.config({ page_title: '首页' });
121
+ // 设置用户属性
122
+ analytics.gtm.setUserProperties({ plan: 'pro' });
133
123
  ```
134
124
 
135
125
  ### 其他方法
136
126
 
137
127
  ```ts
138
- // 立即刷新缓冲区
139
- analytics.flush();
140
-
141
- // 重置用户身份
142
- analytics.reset();
143
-
144
- // 获取会话 ID / 客户端 ID
145
- analytics.getSessionId();
146
- analytics.getClientId();
147
-
148
- // 设置客户端类型 / 商户语言
149
- analytics.setClientType('MINI_PROGRAM');
150
- analytics.setMerchantLanguage('zh');
128
+ analytics.flush(); // 立即刷新缓冲区
129
+ analytics.getSessionId(); // 获取当前 session ID
130
+ analytics.getClientId(); // 获取匿名客户端 ID
131
+ analytics.setClientType('MINI_PROGRAM'); // 设置客户端类型
132
+ analytics.setMerchantLanguage('zh'); // 设置商户语言
133
+ analytics.destroy(); // 销毁实例,移除事件监听(SPA 路由切换时使用)
151
134
  ```
152
135
 
153
136
  ---
154
137
 
155
138
  ## 自动埋点
156
139
 
157
- 默认开启,通过 `data-track-*` 属性声明式埋点:
140
+ 默认开启,通过 `data-track-*` 属性声明式配置:
158
141
 
159
142
  ```html
160
143
  <!-- 自定义事件名 -->
@@ -169,23 +152,48 @@ analytics.setMerchantLanguage('zh');
169
152
  <!-- 任意 data-track-* 前缀自动成为属性键 -->
170
153
  <a data-track-name="share"
171
154
  data-track-platform="wechat"
172
- data-track-position="top">
173
- 分享
174
- </a>
155
+ data-track-position="top">分享</a>
175
156
 
176
157
  <!-- 忽略该元素 -->
177
158
  <div data-track-ignore>不追踪</div>
178
159
  ```
179
160
 
180
- 限定自动埋点范围:
161
+ 限定追踪范围:
181
162
 
182
163
  ```ts
183
164
  createAnalytics({
184
- serverUrl: '...',
185
- autoTrackSelector: '#app', // 只在 #app 内追踪点击
165
+ serverUrl: '...',
166
+ autoTrackSelector: '#app', // 只追踪 #app 内的点击
167
+ });
168
+ ```
169
+
170
+ 关闭自动埋点:
171
+
172
+ ```ts
173
+ createAnalytics({ serverUrl: '...', autoTrack: false });
174
+ ```
175
+
176
+ ---
177
+
178
+ ## 采样控制
179
+
180
+ 通过 `sampleRate` 控制上报比例,降低高流量场景的服务器压力。决策在每次页面加载时独立进行——命中采样的会话全量上报,未命中的会话全部丢弃(包括 GTM)。
181
+
182
+ ```ts
183
+ // 只上报约 10% 的页面会话
184
+ const analytics = createAnalytics({
185
+ appKey: 'your-app-key',
186
+ serverUrl: 'https://your-server.com/collect',
187
+ sampleRate: 0.1,
186
188
  });
187
189
  ```
188
190
 
191
+ - `sampleRate: 1`(默认)— 全量上报
192
+ - `sampleRate: 0` — 全部丢弃,可用于临时关闭上报
193
+ - `sampleRate: 0.1` — 约 10% 的页面会话上报,每次刷新重新决策
194
+
195
+ > 采样决策作用于整个会话:同一次页面加载内,所有 `track()` 要么全部上报,要么全部丢弃,不会出现部分事件缺失的碎片数据。
196
+
189
197
  ---
190
198
 
191
199
  ## 配置项
@@ -193,24 +201,100 @@ createAnalytics({
193
201
  | 配置 | 类型 | 默认值 | 说明 |
194
202
  |---|---|---|---|
195
203
  | `appKey` | `string` | — | 应用标识 |
196
- | `serverUrl` | `string` | — | 数据接收端点 |
197
- | `apiKey` | `string` | — | API Key(不填则根据 `env` 自动解析) |
204
+ | `serverUrl` | `string` | — | 数据接收端点(`disableServer: true` 时可留空) |
205
+ | `apiKey` | `string` | — | API Key(不填则按 `env` 自动解析) |
198
206
  | `env` | `string` | — | 环境:`beta` \| `test` \| `uat` \| `prod` |
199
- | `debug` | `boolean` | `false` | 控制台日志 |
207
+ | `debug` | `boolean` | `false` | 开启控制台日志 |
200
208
  | `autoTrack` | `boolean` | `true` | 自动点击追踪 |
209
+ | `autoTrackPageView` | `boolean` | `true` | 初始化时自动发送 `page_view`;设为 `false` 可手动控制 |
201
210
  | `autoTrackSelector` | `string` | — | 自动埋点的 CSS 选择器范围 |
202
- | `bufferFlushInterval` | `number` | `5000` | 缓冲区刷新间隔(ms) |
203
- | `bufferMaxSize` | `number` | `25` | 缓冲区满即刷新 |
204
- | `maxRetryCount` | `number` | `3` | 发送失败最大重试次数 |
205
- | `sessionTimeout` | `number` | `1800000` | 会话过期时间(ms),默认30分钟 |
206
- | `system` | `string` | | 系统标识 |
207
- | `domain` | `string` | | 域名标识 |
208
- | `performance` | `object` | `{ enabled: true }` | Core Web Vitals 采集配置 |
209
- | `ga.enabled` | `boolean` | | GA 双发开关 |
210
- | `ga.measurementId` | `string` | | GA4 测量 ID(G- 开头) |
211
- | `ga.sendPageView` | `boolean` | `false` | 是否由 gtag 自动发送 page_view |
212
- | `ga.debugMode` | `boolean` | `false` | GA 调试模式 |
213
- | `disableServer` | `boolean` | `false` | 关闭自建服务上报,只发 GA |
211
+ | `sampleRate` | `number` | `1` | 采样率 `0~1`;每次页面加载独立掷骰,`0.1` 表示约 10% 的页面会话上报,`0` 全不报,`1` 全量报 |
212
+ | `system` | `string` | | 系统标识,写入每条事件 |
213
+ | `domain` | `string` | | 业务域标识,写入每条事件 |
214
+ | `keyEvents` | `string[]` | | 关键事件名单,命中的事件立即发送 |
215
+ | `bufferFlushInterval` | `number` | `5000` | 缓冲区定时刷新间隔(ms) |
216
+ | `bufferMaxSize` | `number` | `25` | 缓冲区达到此数量立即刷新 |
217
+ | `maxRetryCount` | `number` | `3` | 发送失败最大重试次数(指数退避) |
218
+ | `sessionTimeout` | `number` | `1800000` | 会话超时(ms),默认 30 分钟 |
219
+ | `performance.enabled` | `boolean` | `true` | Core Web Vitals 采集开关 |
220
+ | `gtm.enabled` | `boolean` | | GTM 双发开关 |
221
+ | `gtm.containerId` | `string` | | GTM 容器 ID(`GTM-XXXXXXX` 格式) |
222
+ | `gtm.dataLayerName` | `string` | `'dataLayer'` | 自定义 dataLayer 变量名 |
223
+ | `disableServer` | `boolean` | `false` | 关闭自建服务上报,只推 GTM |
224
+
225
+ ---
226
+
227
+ ## 上报数据结构
228
+
229
+ ### 自建服务(批量 POST)
230
+
231
+ ```json
232
+ {
233
+ "client_id": "uuid",
234
+ "user_id": "user-123",
235
+ "session_id": "uuid",
236
+ "user_properties": { "plan": "pro" },
237
+ "context": {
238
+ "sdk_version": "1.0.0",
239
+ "app_key": "your-app-key",
240
+ "browser": { "name": "Chrome", "version": "135" },
241
+ "operating_system": { "name": "Windows", "version": "10" },
242
+ "device_type": "desktop",
243
+ "country": "CN",
244
+ "language": "zh-CN",
245
+ "timezone": "Asia/Shanghai"
246
+ },
247
+ "marketing": {
248
+ "utm_source": "google",
249
+ "utm_medium": "cpc",
250
+ "utm_campaign": "spring_sale",
251
+ "landing_page": "https://example.com/?utm_source=google",
252
+ "referrer_url": "https://google.com"
253
+ },
254
+ "events": [
255
+ {
256
+ "event_id": "uuid",
257
+ "event_name": "button_click",
258
+ "timestamp": 1700000000000,
259
+ "key_event": false,
260
+ "batch_event_index": 0,
261
+ "batch_ordering_id": "1700000000000-1",
262
+ "batch_page_id": "abc-xyz",
263
+ "event_properties": {
264
+ "system": "web",
265
+ "domain": "payment",
266
+ "scene": "product",
267
+ "action": "click",
268
+ "target_type": "button",
269
+ "target_id": "buy_btn",
270
+ "page_url": "https://example.com/product"
271
+ }
272
+ }
273
+ ]
274
+ }
275
+ ```
276
+
277
+ ### GTM dataLayer
278
+
279
+ 每个 `track()` 调用推入一条:
280
+
281
+ ```json
282
+ {
283
+ "event": "button_click",
284
+ "action": "click",
285
+ "target_id": "buy_btn",
286
+ "session_id": "uuid",
287
+ "page_url": "https://example.com/product",
288
+ "referrer": ""
289
+ }
290
+ ```
291
+
292
+ `identify()` 推入:
293
+
294
+ ```json
295
+ { "userId": "user-123" }
296
+ { "plan": "pro", "email": "user@example.com" }
297
+ ```
214
298
 
215
299
  ---
216
300
 
@@ -218,20 +302,36 @@ createAnalytics({
218
302
 
219
303
  | 采集项 | 说明 |
220
304
  |---|---|
221
- | 点击事件 | 自动埋点 + 手动 `analytics.track()` |
222
- | 页面浏览 | 初始化时自动发送 `page_view` |
223
- | JS 错误 | 全局 `window.error` / `unhandledrejection` |
224
- | Core Web Vitals | LCP / FCP / CLS / INP / TTFB |
225
- | 设备上下文 | 浏览器、OS、屏幕、语言、时区等 |
226
- | 营销归因 | UTM 参数、落地页、来源 URL |
305
+ | 点击事件 | 自动埋点(`data-track-*`)+ 手动 `analytics.track()` |
306
+ | 页面浏览 | 初始化时自动发送 `page_view`(可通过 `autoTrackPageView: false` 关闭) |
307
+ | JS 错误 | 全局 `window.error` / `unhandledrejection` 自动捕获 |
308
+ | Core Web Vitals | LCP / FCP / CLS / INP / TTFB(`web_vital` 事件) |
309
+ | 设备上下文 | 浏览器、OS、屏幕分辨率、语言、时区、设备类型 |
310
+ | 营销归因 | UTM 参数持久化、落地页、来源 URL(SPA 路由切换后仍保留) |
227
311
 
228
312
  ---
229
313
 
230
- ## 双发机制
314
+ ## 可靠性机制
231
315
 
232
- 每个 `analytics.track()` 调用同时发送到两处:
316
+ - **缓冲区 + 批量发送**:事件先入缓冲区,满 `bufferMaxSize` 或到 `bufferFlushInterval` 后批量发送
317
+ - **指数退避重试**:发送失败最多重试 `maxRetryCount` 次,间隔 1s / 2s / 4s…
318
+ - **本地持久化**:超出重试次数的事件写入 localStorage,下次页面加载时自动恢复补发(24 小时内有效)
319
+ - **页面关闭兜底**:`beforeunload` / `pagehide` 时用 `sendBeacon` 发送缓冲区剩余事件
320
+ - **采样控制**:`sampleRate` 在客户端决定是否上报,降低高流量场景的服务器压力
233
321
 
234
- 1. **自建服务** — 事件进入缓冲区,定时批量 POST 到 `serverUrl`
235
- 2. **GA4** — 即时调用 `gtag('event', ...)`,附带 session_id、页面信息、UTM 参数
322
+ ---
323
+
324
+ ## SPA 使用建议
325
+
326
+ 路由切换时销毁旧实例,重建新实例以正确发送新页面的 `page_view`:
236
327
 
237
- GA 初始化失败会自动降级,不影响自建服务上报。
328
+ ```ts
329
+ // 路由离开时
330
+ analytics.destroy();
331
+
332
+ // 路由进入时
333
+ analytics = createAnalytics({ ... });
334
+ // 或手动控制
335
+ analytics = createAnalytics({ ..., autoTrackPageView: false });
336
+ analytics.track('page_view', { page_url: location.href });
337
+ ```