behavior-aliu 1.0.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 ADDED
@@ -0,0 +1,104 @@
1
+ # User Behavior Monitor SDK
2
+
3
+ 轻量级、零依赖的前端用户行为监控 SDK。
4
+ 专为现代 Web 应用设计,支持 PV/UV 统计、精准停留时长计算、用户点击行为追踪以及 SPA(单页应用)路由监控。
5
+
6
+ ## 核心特性
7
+
8
+ - ** 基础指标**: 自动采集 **PV** (Page View) 和 **UV** (Unique Visitor)。
9
+ - ** 精准停留**: 基于 `visibilitychange` 和 `beforeunload` 精准计算页面实际停留时长。
10
+ - ** 点击追踪**: 通过 `data-track-click` 属性实现无侵入式全自动点击埋点。
11
+ - ** SPA 支持**: 完美支持 Vue/React 等单页应用的路由跳转监控(Hash & History 模式)。
12
+ - ** 稳健上报**: 优先使用 `navigator.sendBeacon` 确保页面关闭时数据不丢失,自动降级 `fetch`。
13
+
14
+ ## 安装
15
+
16
+ ```bash
17
+ npm install
18
+ npm run build
19
+ ```
20
+
21
+ ## 快速开始
22
+
23
+ ### 1. 引入 SDK
24
+
25
+ 支持 ES Module (推荐) 或 Script 标签引入。
26
+
27
+ ```javascript
28
+ import { initUserBehaviorMonitor } from './dist/index.esm.js';
29
+
30
+ initUserBehaviorMonitor({
31
+ projectName: 'my-awesome-project', // 项目标识
32
+ reportUrl: 'http://your-server.com/report', // 数据上报地址
33
+ });
34
+ ```
35
+
36
+ ### 2. 自动捕获点击
37
+
38
+ 只需在 HTML 元素上添加 `data-track-click` 属性,SDK 会自动捕获点击并上报。
39
+
40
+ ```html
41
+ <!-- 点击后自动上报: { behavior: 'click', action: 'buy_btn', ... } -->
42
+ <button data-track-click="buy_btn">立即购买</button>
43
+ ```
44
+
45
+ ## 运行 Demo
46
+
47
+ 本项目内置了一个基于 Express 的测试环境,方便您直观体验监控效果。
48
+
49
+ 1. **安装依赖**
50
+
51
+ ```bash
52
+ npm install
53
+ ```
54
+
55
+ 2. **构建 SDK**
56
+
57
+ ```bash
58
+ npm run build
59
+ ```
60
+
61
+ 3. **启动 Demo**
62
+
63
+ ```bash
64
+ npm run demo
65
+ ```
66
+
67
+ > 终端会显示:`测试服务器已启动! 👉 页面地址: http://localhost:3000/index.html`
68
+
69
+ 4. **体验功能**
70
+ - 打开浏览器访问 `http://localhost:3000/index.html`
71
+ - **点击按钮**: 观察终端输出 `behavior: 'click'`
72
+ - **模拟路由跳转**: 点击 "模拟路由跳转",观察 `dwell` (旧页面停留) 和 `pv` (新页面) 上报
73
+ - **刷新/关闭页面**: 观察 `dwell` (页面卸载) 上报
74
+
75
+ ## 数据格式示例
76
+
77
+ SDK 上报的数据格式如下 (JSON):
78
+
79
+ **PV (页面访问)**
80
+
81
+ ```json
82
+ {
83
+ "behavior": "pv",
84
+ "projectName": "demo",
85
+ "pageUrl": "http://localhost:3000/",
86
+ "pv": 15,
87
+ "userId": "xxxx-xxxx"
88
+ }
89
+ ```
90
+
91
+ **Dwell (停留时长)**
92
+
93
+ ```json
94
+ {
95
+ "behavior": "dwell",
96
+ "dwellTime": 4520, // 毫秒
97
+ "pageUrl": "http://localhost:3000/"
98
+ }
99
+ ```
100
+
101
+ ## 开发
102
+
103
+ - `npm run dev`: 监听源码变化并自动重新构建
104
+ - `npm run build`: 生产环境构建 (输出到 `dist/`)
@@ -0,0 +1,252 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * 发送用户行为数据
5
+ * @param data - 用户行为数据
6
+ * @param url - 数据上报的URL
7
+ */
8
+ var sendBehaviorData = function (data, url) {
9
+ if (navigator.sendBeacon) {
10
+ var blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
11
+ navigator.sendBeacon(url, blob);
12
+ }
13
+ else {
14
+ fetch(url, {
15
+ method: 'POST',
16
+ headers: { 'Content-Type': 'application/json' },
17
+ body: JSON.stringify(data),
18
+ }).catch(function (error) { return console.error('Error sending behavior data:', error); });
19
+ }
20
+ };
21
+
22
+ var USER_ID_KEY = 'user_behavior_user_id';
23
+ var PV_COUNT_KEY = 'user_behavior_pv_count';
24
+ var UV_STORAGE_KEY = 'user_behavior_uv';
25
+ // 获取用户唯一标识(UUID)​
26
+ var getUserID = function () {
27
+ var userId = localStorage.getItem(USER_ID_KEY);
28
+ if (!userId) {
29
+ userId = generateUUID();
30
+ localStorage.setItem(USER_ID_KEY, userId);
31
+ }
32
+ return userId;
33
+ };
34
+ /**
35
+ * @description: 生成唯一标识符
36
+ * @return {string} 唯一标识符
37
+ */
38
+ var generateUUID = function () {
39
+ return 'xxxx-xxxx-4xxx-yxxx-xxxx'.replace(/[xy]/g, function (char) {
40
+ var random = (Math.random() * 16) | 0;
41
+ var value = char === 'x' ? random : (random & 0x3) | 0x8;
42
+ return value.toString(16);
43
+ });
44
+ };
45
+ var incrementPV = function () {
46
+ var today = new Date().toISOString().split('T')[0]; // 获取当天的日期​
47
+ var pvData = localStorage.getItem("".concat(PV_COUNT_KEY, "_").concat(today));
48
+ var newPV = (pvData ? parseInt(pvData, 10) : 0) + 1;
49
+ localStorage.setItem("".concat(PV_COUNT_KEY, "_").concat(today), newPV.toString());
50
+ return newPV;
51
+ };
52
+ // 检查是否已经记录 UV​
53
+ var isUVRecorded = function () {
54
+ var today = new Date().toISOString().split('T')[0];
55
+ return localStorage.getItem(UV_STORAGE_KEY) === today;
56
+ };
57
+ // 设置 UV 记录​
58
+ var setUVRecorded = function () {
59
+ var today = new Date().toISOString().split('T')[0];
60
+ localStorage.setItem(UV_STORAGE_KEY, today);
61
+ };
62
+
63
+ // 页面加载时间
64
+ var pageLoadTime = Date.now();
65
+ // 上一个页面的 URL
66
+ var lastPageUrl = window.location.href;
67
+ var lastDwellReportedForLoadTime = null;
68
+ /**
69
+ * @description: 跟踪用户行为(用户点击、MPA 首次加载 PV、通用停留时间、SPA 路由行为)
70
+ * @param projectName 项目名称
71
+ * @param reportUrl 上报 URL
72
+ */
73
+ var trackUserBehavior = function (projectName, reportUrl) {
74
+ // 1. 通过事件委托捕获点击
75
+ trackClicks(projectName, reportUrl);
76
+ // 2. MPA 首次加载(页面初次进入)PV 上报
77
+ trackMpaPageView(projectName, reportUrl);
78
+ // 3. 通用停留时间(关闭页面/切换标签)上报
79
+ trackPageDwellTime(projectName, reportUrl);
80
+ // 4. SPA 路由行为(路由变化:先上报旧页面停留,再上报新页面 PV)
81
+ trackSpaBehavior(projectName, reportUrl);
82
+ };
83
+ /**
84
+ * @description: 上报页面停留时间
85
+ * @param projectName 项目名称
86
+ * @param reportUrl 上报 URL
87
+ * @returns
88
+ */
89
+ var reportDwellTime = function (projectName, reportUrl) {
90
+ if (lastDwellReportedForLoadTime === pageLoadTime)
91
+ return;
92
+ var dwellTime = Date.now() - pageLoadTime;
93
+ if (dwellTime > 0) {
94
+ sendBehaviorData({
95
+ behavior: 'dwell',
96
+ userId: getUserID(),
97
+ projectName: projectName,
98
+ timestamp: new Date().toISOString(),
99
+ pageUrl: lastPageUrl,
100
+ dwellTime: dwellTime,
101
+ }, reportUrl);
102
+ lastDwellReportedForLoadTime = pageLoadTime;
103
+ }
104
+ };
105
+ /**
106
+ * 捕获首屏 PV(MPA传统网页)
107
+ * @param projectName 项目名称
108
+ * @param reportUrl 上报 URL
109
+ */
110
+ var trackMpaPageView = function (projectName, reportUrl) {
111
+ window.addEventListener('load', function () {
112
+ var userId = getUserID();
113
+ var pv = incrementPV(); // 增加 PV 计数
114
+ // 发送 PV 数据
115
+ sendBehaviorData({
116
+ behavior: 'pv',
117
+ userId: userId,
118
+ projectName: projectName,
119
+ timestamp: new Date().toISOString(),
120
+ pageUrl: window.location.href,
121
+ referrer: document.referrer || '', // 记录来源
122
+ pv: pv,
123
+ }, reportUrl);
124
+ // 记录页面加载时间
125
+ pageLoadTime = Date.now();
126
+ lastPageUrl = window.location.href;
127
+ });
128
+ };
129
+ /**
130
+ * @description: 捕获点击事件,通过 data-track-click 属性简化识别​
131
+ * @param projectName 项目名称
132
+ * @param reportUrl 上报 URL
133
+ * @returns
134
+ */
135
+ var trackClicks = function (projectName, reportUrl) {
136
+ document.addEventListener('click', function (event) {
137
+ var target = event.target;
138
+ // 如果目标元素带有 data-track-click 属性​
139
+ if (target && target.dataset.trackClick) {
140
+ var behaviorData = {
141
+ behavior: 'click',
142
+ userId: getUserID(),
143
+ projectName: projectName,
144
+ timestamp: new Date().toISOString(),
145
+ element: target.tagName,
146
+ action: target.dataset.trackClick, // 自定义的点击行为​
147
+ pageUrl: window.location.href,
148
+ referrer: lastPageUrl, // 记录点击时的页面来源
149
+ };
150
+ // 发送点击事件数据到服务端​
151
+ sendBehaviorData(behaviorData, reportUrl);
152
+ }
153
+ });
154
+ };
155
+ /**
156
+ * @description: 捕获页面停留时间(关闭/隐藏)
157
+ * @param projectName 项目名称
158
+ * @param reportUrl 上报 URL
159
+ * @returns
160
+ */
161
+ var trackPageDwellTime = function (projectName, reportUrl) {
162
+ // 在 beforeunload 时计算停留时间
163
+ window.addEventListener('beforeunload', function () {
164
+ reportDwellTime(projectName, reportUrl);
165
+ });
166
+ window.addEventListener('pagehide', function () {
167
+ reportDwellTime(projectName, reportUrl);
168
+ });
169
+ // 在 visibilitychange(切换标签)时计算停留时间
170
+ document.addEventListener('visibilitychange', function () {
171
+ if (document.visibilityState === 'hidden') {
172
+ reportDwellTime(projectName, reportUrl);
173
+ }
174
+ });
175
+ };
176
+ /**
177
+ * @description: 捕获 SPA 路由行为(路由变化:先上报旧页面停留,再上报新页面 PV)
178
+ * @param projectName 项目名称
179
+ * @param reportUrl 上报 URL
180
+ * @returns
181
+ */
182
+ var trackSpaBehavior = function (projectName, reportUrl) {
183
+ /**
184
+ * @description: 处理路由变化事件回调
185
+ * @returns
186
+ */
187
+ var handleRouteChange = function () {
188
+ // 1. 防抖校验:如果 URL 没变(比如 hashchange 和 popstate 同时触发),直接退出
189
+ if (window.location.href === lastPageUrl)
190
+ return;
191
+ // 2. 结算上一页:上报前一个页面的停留时间
192
+ reportDwellTime(projectName, reportUrl);
193
+ // 3. 记录当前 URL 为 referrer (在更新 lastPageUrl 之前!)
194
+ var referrer = lastPageUrl;
195
+ // 4. 更新状态:保存当前 URL,为下一次跳转做准备
196
+ pageLoadTime = Date.now();
197
+ lastPageUrl = window.location.href;
198
+ // 5. 记录新页面:上报 PV
199
+ var userId = getUserID();
200
+ var pv = incrementPV();
201
+ sendBehaviorData({
202
+ behavior: 'pv',
203
+ userId: userId,
204
+ projectName: projectName,
205
+ timestamp: new Date().toISOString(),
206
+ pageUrl: window.location.href,
207
+ referrer: referrer, // 这里的 referrer 是跳转前的页面 URL
208
+ pv: pv,
209
+ }, reportUrl);
210
+ };
211
+ window.addEventListener('hashchange', handleRouteChange);
212
+ window.addEventListener('popstate', handleRouteChange);
213
+ var originalPush = history.pushState;
214
+ var originalReplace = history.replaceState;
215
+ history.pushState = function () {
216
+ var args = [];
217
+ for (var _i = 0; _i < arguments.length; _i++) {
218
+ args[_i] = arguments[_i];
219
+ }
220
+ originalPush.apply(this, args);
221
+ handleRouteChange();
222
+ };
223
+ history.replaceState = function () {
224
+ var args = [];
225
+ for (var _i = 0; _i < arguments.length; _i++) {
226
+ args[_i] = arguments[_i];
227
+ }
228
+ originalReplace.apply(this, args);
229
+ handleRouteChange();
230
+ };
231
+ };
232
+
233
+ // 初始化用户行为监控​
234
+ var initUserBehaviorMonitor = function (_a) {
235
+ var projectName = _a.projectName, reportUrl = _a.reportUrl;
236
+ var userId = getUserID();
237
+ // UV 统计:如果是用户首次访问,记录 UV​
238
+ if (!isUVRecorded()) {
239
+ sendBehaviorData({
240
+ behavior: 'uv',
241
+ userId: userId,
242
+ projectName: projectName,
243
+ timestamp: new Date().toISOString(),
244
+ }, reportUrl);
245
+ setUVRecorded();
246
+ }
247
+ // 启动点击行为、PV 统计和页面停留时间监控​
248
+ trackUserBehavior(projectName, reportUrl);
249
+ };
250
+
251
+ exports.initUserBehaviorMonitor = initUserBehaviorMonitor;
252
+ //# sourceMappingURL=index.cjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/sender.ts","../src/storage.ts","../src/tracker.ts","../src/index.ts"],"sourcesContent":["/**\r\n * 发送用户行为数据\r\n * @param data - 用户行为数据\r\n * @param url - 数据上报的URL\r\n */\r\nexport const sendBehaviorData = (data: Record<string, any>, url: string) => {\r\n if (navigator.sendBeacon) {\r\n const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });\r\n navigator.sendBeacon(url, blob);\r\n } else {\r\n fetch(url, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(data),\r\n }).catch((error) => console.error('Error sending behavior data:', error));\r\n }\r\n};","const USER_ID_KEY = 'user_behavior_user_id';\r\nconst PV_COUNT_KEY = 'user_behavior_pv_count';\r\nconst UV_STORAGE_KEY = 'user_behavior_uv';\r\n\r\n// 获取用户唯一标识(UUID)​\r\nexport const getUserID = (): string => {\r\n let userId = localStorage.getItem(USER_ID_KEY);\r\n if (!userId) {\r\n userId = generateUUID();\r\n localStorage.setItem(USER_ID_KEY, userId);\r\n }\r\n return userId;\r\n};\r\n\r\n/**\r\n * @description: 生成唯一标识符\r\n * @return {string} 唯一标识符\r\n */\r\nconst generateUUID = (): string => {\r\n return 'xxxx-xxxx-4xxx-yxxx-xxxx'.replace(/[xy]/g, (char) => {\r\n const random = (Math.random() * 16) | 0;\r\n const value = char === 'x' ? random : (random & 0x3) | 0x8;\r\n return value.toString(16);\r\n });\r\n};\r\n\r\n\r\nexport const incrementPV = (): number => {\r\n const today = new Date().toISOString().split('T')[0]; // 获取当天的日期​\r\n const pvData = localStorage.getItem(`${PV_COUNT_KEY}_${today}`);\r\n const newPV = (pvData ? parseInt(pvData, 10) : 0) + 1;\r\n localStorage.setItem(`${PV_COUNT_KEY}_${today}`, newPV.toString());\r\n return newPV;\r\n};\r\n\r\n\r\n// 检查是否已经记录 UV​\r\nexport const isUVRecorded = (): boolean => {\r\n const today = new Date().toISOString().split('T')[0];\r\n return localStorage.getItem(UV_STORAGE_KEY) === today;\r\n};\r\n\r\n\r\n// 设置 UV 记录​\r\nexport const setUVRecorded = () => {\r\n const today = new Date().toISOString().split('T')[0];\r\n localStorage.setItem(UV_STORAGE_KEY, today);\r\n};","import { sendBehaviorData } from './sender';\r\nimport { getUserID, incrementPV } from './storage';\r\n\r\n// 页面加载时间\r\nlet pageLoadTime: number = Date.now();\r\n// 上一个页面的 URL\r\nlet lastPageUrl: string = window.location.href;\r\nlet lastDwellReportedForLoadTime: number | null = null;\r\n\r\n\r\n/**\r\n * @description: 跟踪用户行为(用户点击、MPA 首次加载 PV、通用停留时间、SPA 路由行为)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n */\r\nexport const trackUserBehavior = (projectName: string, reportUrl: string) => {\r\n // 1. 通过事件委托捕获点击\r\n trackClicks(projectName, reportUrl);\r\n\r\n // 2. MPA 首次加载(页面初次进入)PV 上报\r\n trackMpaPageView(projectName, reportUrl);\r\n\r\n // 3. 通用停留时间(关闭页面/切换标签)上报\r\n trackPageDwellTime(projectName, reportUrl);\r\n\r\n // 4. SPA 路由行为(路由变化:先上报旧页面停留,再上报新页面 PV)\r\n trackSpaBehavior(projectName, reportUrl);\r\n};\r\n\r\n/**\r\n * @description: 上报页面停留时间\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst reportDwellTime = (projectName: string, reportUrl: string) => {\r\n if (lastDwellReportedForLoadTime === pageLoadTime) return;\r\n const dwellTime = Date.now() - pageLoadTime;\r\n if (dwellTime > 0) {\r\n sendBehaviorData({\r\n behavior: 'dwell',\r\n userId: getUserID(),\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n pageUrl: lastPageUrl,\r\n dwellTime,\r\n }, reportUrl);\r\n lastDwellReportedForLoadTime = pageLoadTime;\r\n }\r\n};\r\n\r\n\r\n/**\r\n * 捕获首屏 PV(MPA传统网页)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n */\r\nconst trackMpaPageView = (projectName: string, reportUrl: string) => {\r\n window.addEventListener('load', () => {\r\n const userId = getUserID();\r\n const pv = incrementPV(); // 增加 PV 计数\r\n\r\n // 发送 PV 数据\r\n sendBehaviorData({\r\n behavior: 'pv',\r\n userId,\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n pageUrl: window.location.href,\r\n referrer: document.referrer || '', // 记录来源\r\n pv,\r\n }, reportUrl);\r\n\r\n // 记录页面加载时间\r\n pageLoadTime = Date.now();\r\n lastPageUrl = window.location.href;\r\n });\r\n};\r\n\r\n\r\n/**\r\n * @description: 捕获点击事件,通过 data-track-click 属性简化识别​\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst trackClicks = (projectName: string, reportUrl: string) => {\r\n document.addEventListener('click', (event) => {\r\n const target = event.target as HTMLElement;\r\n\r\n // 如果目标元素带有 data-track-click 属性​\r\n if (target && target.dataset.trackClick) {\r\n const behaviorData = {\r\n behavior: 'click',\r\n userId: getUserID(),\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n element: target.tagName,\r\n action: target.dataset.trackClick, // 自定义的点击行为​\r\n pageUrl: window.location.href,\r\n referrer: lastPageUrl, // 记录点击时的页面来源\r\n };\r\n\r\n // 发送点击事件数据到服务端​\r\n sendBehaviorData(behaviorData, reportUrl);\r\n }\r\n });\r\n};\r\n\r\n\r\n\r\n/**\r\n * @description: 捕获页面停留时间(关闭/隐藏)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst trackPageDwellTime = (projectName: string, reportUrl: string) => {\r\n // 在 beforeunload 时计算停留时间\r\n window.addEventListener('beforeunload', () => {\r\n reportDwellTime(projectName, reportUrl);\r\n });\r\n\r\n window.addEventListener('pagehide', () => {\r\n reportDwellTime(projectName, reportUrl);\r\n });\r\n\r\n // 在 visibilitychange(切换标签)时计算停留时间\r\n document.addEventListener('visibilitychange', () => {\r\n if (document.visibilityState === 'hidden') {\r\n reportDwellTime(projectName, reportUrl);\r\n }\r\n });\r\n};\r\n\r\n/**\r\n * @description: 捕获 SPA 路由行为(路由变化:先上报旧页面停留,再上报新页面 PV)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst trackSpaBehavior = (projectName: string, reportUrl: string) => {\r\n /**\r\n * @description: 处理路由变化事件回调\r\n * @returns \r\n */\r\n const handleRouteChange = () => {\r\n // 1. 防抖校验:如果 URL 没变(比如 hashchange 和 popstate 同时触发),直接退出\r\n if (window.location.href === lastPageUrl) return;\r\n\r\n // 2. 结算上一页:上报前一个页面的停留时间\r\n reportDwellTime(projectName, reportUrl);\r\n\r\n // 3. 记录当前 URL 为 referrer (在更新 lastPageUrl 之前!)\r\n const referrer = lastPageUrl;\r\n\r\n // 4. 更新状态:保存当前 URL,为下一次跳转做准备\r\n pageLoadTime = Date.now();\r\n lastPageUrl = window.location.href;\r\n\r\n // 5. 记录新页面:上报 PV\r\n const userId = getUserID();\r\n const pv = incrementPV();\r\n sendBehaviorData({\r\n behavior: 'pv',\r\n userId,\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n pageUrl: window.location.href,\r\n referrer: referrer, // 这里的 referrer 是跳转前的页面 URL\r\n pv,\r\n }, reportUrl);\r\n };\r\n\r\n window.addEventListener('hashchange', handleRouteChange);\r\n window.addEventListener('popstate', handleRouteChange);\r\n\r\n const originalPush = history.pushState;\r\n const originalReplace = history.replaceState;\r\n\r\n history.pushState = function (...args: Parameters<typeof history.pushState>) {\r\n originalPush.apply(this, args);\r\n handleRouteChange();\r\n };\r\n\r\n history.replaceState = function (...args: Parameters<typeof history.replaceState>) {\r\n originalReplace.apply(this, args);\r\n handleRouteChange();\r\n };\r\n};","\r\nimport { trackUserBehavior } from './tracker';\r\nimport { getUserID, isUVRecorded, setUVRecorded } from './storage';\r\nimport { sendBehaviorData } from './sender';\r\n\r\ninterface MonitorConfig {\r\n projectName: string; // 当前项目名称,用于区分项目​\r\n reportUrl: string; // 上报服务器的地址​\r\n}\r\n\r\n// 初始化用户行为监控​\r\nexport const initUserBehaviorMonitor = ({ projectName, reportUrl }: MonitorConfig) => {\r\n const userId = getUserID();\r\n\r\n // UV 统计:如果是用户首次访问,记录 UV​\r\n if (!isUVRecorded()) {\r\n sendBehaviorData({\r\n behavior: 'uv',\r\n userId,\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n }, reportUrl);\r\n setUVRecorded();\r\n }\r\n\r\n // 启动点击行为、PV 统计和页面停留时间监控​\r\n trackUserBehavior(projectName, reportUrl);\r\n};​"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ interface MonitorConfig {
2
+ projectName: string;
3
+ reportUrl: string;
4
+ }
5
+ export declare const initUserBehaviorMonitor: ({ projectName, reportUrl }: MonitorConfig) => void;
6
+ export {};
@@ -0,0 +1,250 @@
1
+ /**
2
+ * 发送用户行为数据
3
+ * @param data - 用户行为数据
4
+ * @param url - 数据上报的URL
5
+ */
6
+ var sendBehaviorData = function (data, url) {
7
+ if (navigator.sendBeacon) {
8
+ var blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
9
+ navigator.sendBeacon(url, blob);
10
+ }
11
+ else {
12
+ fetch(url, {
13
+ method: 'POST',
14
+ headers: { 'Content-Type': 'application/json' },
15
+ body: JSON.stringify(data),
16
+ }).catch(function (error) { return console.error('Error sending behavior data:', error); });
17
+ }
18
+ };
19
+
20
+ var USER_ID_KEY = 'user_behavior_user_id';
21
+ var PV_COUNT_KEY = 'user_behavior_pv_count';
22
+ var UV_STORAGE_KEY = 'user_behavior_uv';
23
+ // 获取用户唯一标识(UUID)​
24
+ var getUserID = function () {
25
+ var userId = localStorage.getItem(USER_ID_KEY);
26
+ if (!userId) {
27
+ userId = generateUUID();
28
+ localStorage.setItem(USER_ID_KEY, userId);
29
+ }
30
+ return userId;
31
+ };
32
+ /**
33
+ * @description: 生成唯一标识符
34
+ * @return {string} 唯一标识符
35
+ */
36
+ var generateUUID = function () {
37
+ return 'xxxx-xxxx-4xxx-yxxx-xxxx'.replace(/[xy]/g, function (char) {
38
+ var random = (Math.random() * 16) | 0;
39
+ var value = char === 'x' ? random : (random & 0x3) | 0x8;
40
+ return value.toString(16);
41
+ });
42
+ };
43
+ var incrementPV = function () {
44
+ var today = new Date().toISOString().split('T')[0]; // 获取当天的日期​
45
+ var pvData = localStorage.getItem("".concat(PV_COUNT_KEY, "_").concat(today));
46
+ var newPV = (pvData ? parseInt(pvData, 10) : 0) + 1;
47
+ localStorage.setItem("".concat(PV_COUNT_KEY, "_").concat(today), newPV.toString());
48
+ return newPV;
49
+ };
50
+ // 检查是否已经记录 UV​
51
+ var isUVRecorded = function () {
52
+ var today = new Date().toISOString().split('T')[0];
53
+ return localStorage.getItem(UV_STORAGE_KEY) === today;
54
+ };
55
+ // 设置 UV 记录​
56
+ var setUVRecorded = function () {
57
+ var today = new Date().toISOString().split('T')[0];
58
+ localStorage.setItem(UV_STORAGE_KEY, today);
59
+ };
60
+
61
+ // 页面加载时间
62
+ var pageLoadTime = Date.now();
63
+ // 上一个页面的 URL
64
+ var lastPageUrl = window.location.href;
65
+ var lastDwellReportedForLoadTime = null;
66
+ /**
67
+ * @description: 跟踪用户行为(用户点击、MPA 首次加载 PV、通用停留时间、SPA 路由行为)
68
+ * @param projectName 项目名称
69
+ * @param reportUrl 上报 URL
70
+ */
71
+ var trackUserBehavior = function (projectName, reportUrl) {
72
+ // 1. 通过事件委托捕获点击
73
+ trackClicks(projectName, reportUrl);
74
+ // 2. MPA 首次加载(页面初次进入)PV 上报
75
+ trackMpaPageView(projectName, reportUrl);
76
+ // 3. 通用停留时间(关闭页面/切换标签)上报
77
+ trackPageDwellTime(projectName, reportUrl);
78
+ // 4. SPA 路由行为(路由变化:先上报旧页面停留,再上报新页面 PV)
79
+ trackSpaBehavior(projectName, reportUrl);
80
+ };
81
+ /**
82
+ * @description: 上报页面停留时间
83
+ * @param projectName 项目名称
84
+ * @param reportUrl 上报 URL
85
+ * @returns
86
+ */
87
+ var reportDwellTime = function (projectName, reportUrl) {
88
+ if (lastDwellReportedForLoadTime === pageLoadTime)
89
+ return;
90
+ var dwellTime = Date.now() - pageLoadTime;
91
+ if (dwellTime > 0) {
92
+ sendBehaviorData({
93
+ behavior: 'dwell',
94
+ userId: getUserID(),
95
+ projectName: projectName,
96
+ timestamp: new Date().toISOString(),
97
+ pageUrl: lastPageUrl,
98
+ dwellTime: dwellTime,
99
+ }, reportUrl);
100
+ lastDwellReportedForLoadTime = pageLoadTime;
101
+ }
102
+ };
103
+ /**
104
+ * 捕获首屏 PV(MPA传统网页)
105
+ * @param projectName 项目名称
106
+ * @param reportUrl 上报 URL
107
+ */
108
+ var trackMpaPageView = function (projectName, reportUrl) {
109
+ window.addEventListener('load', function () {
110
+ var userId = getUserID();
111
+ var pv = incrementPV(); // 增加 PV 计数
112
+ // 发送 PV 数据
113
+ sendBehaviorData({
114
+ behavior: 'pv',
115
+ userId: userId,
116
+ projectName: projectName,
117
+ timestamp: new Date().toISOString(),
118
+ pageUrl: window.location.href,
119
+ referrer: document.referrer || '', // 记录来源
120
+ pv: pv,
121
+ }, reportUrl);
122
+ // 记录页面加载时间
123
+ pageLoadTime = Date.now();
124
+ lastPageUrl = window.location.href;
125
+ });
126
+ };
127
+ /**
128
+ * @description: 捕获点击事件,通过 data-track-click 属性简化识别​
129
+ * @param projectName 项目名称
130
+ * @param reportUrl 上报 URL
131
+ * @returns
132
+ */
133
+ var trackClicks = function (projectName, reportUrl) {
134
+ document.addEventListener('click', function (event) {
135
+ var target = event.target;
136
+ // 如果目标元素带有 data-track-click 属性​
137
+ if (target && target.dataset.trackClick) {
138
+ var behaviorData = {
139
+ behavior: 'click',
140
+ userId: getUserID(),
141
+ projectName: projectName,
142
+ timestamp: new Date().toISOString(),
143
+ element: target.tagName,
144
+ action: target.dataset.trackClick, // 自定义的点击行为​
145
+ pageUrl: window.location.href,
146
+ referrer: lastPageUrl, // 记录点击时的页面来源
147
+ };
148
+ // 发送点击事件数据到服务端​
149
+ sendBehaviorData(behaviorData, reportUrl);
150
+ }
151
+ });
152
+ };
153
+ /**
154
+ * @description: 捕获页面停留时间(关闭/隐藏)
155
+ * @param projectName 项目名称
156
+ * @param reportUrl 上报 URL
157
+ * @returns
158
+ */
159
+ var trackPageDwellTime = function (projectName, reportUrl) {
160
+ // 在 beforeunload 时计算停留时间
161
+ window.addEventListener('beforeunload', function () {
162
+ reportDwellTime(projectName, reportUrl);
163
+ });
164
+ window.addEventListener('pagehide', function () {
165
+ reportDwellTime(projectName, reportUrl);
166
+ });
167
+ // 在 visibilitychange(切换标签)时计算停留时间
168
+ document.addEventListener('visibilitychange', function () {
169
+ if (document.visibilityState === 'hidden') {
170
+ reportDwellTime(projectName, reportUrl);
171
+ }
172
+ });
173
+ };
174
+ /**
175
+ * @description: 捕获 SPA 路由行为(路由变化:先上报旧页面停留,再上报新页面 PV)
176
+ * @param projectName 项目名称
177
+ * @param reportUrl 上报 URL
178
+ * @returns
179
+ */
180
+ var trackSpaBehavior = function (projectName, reportUrl) {
181
+ /**
182
+ * @description: 处理路由变化事件回调
183
+ * @returns
184
+ */
185
+ var handleRouteChange = function () {
186
+ // 1. 防抖校验:如果 URL 没变(比如 hashchange 和 popstate 同时触发),直接退出
187
+ if (window.location.href === lastPageUrl)
188
+ return;
189
+ // 2. 结算上一页:上报前一个页面的停留时间
190
+ reportDwellTime(projectName, reportUrl);
191
+ // 3. 记录当前 URL 为 referrer (在更新 lastPageUrl 之前!)
192
+ var referrer = lastPageUrl;
193
+ // 4. 更新状态:保存当前 URL,为下一次跳转做准备
194
+ pageLoadTime = Date.now();
195
+ lastPageUrl = window.location.href;
196
+ // 5. 记录新页面:上报 PV
197
+ var userId = getUserID();
198
+ var pv = incrementPV();
199
+ sendBehaviorData({
200
+ behavior: 'pv',
201
+ userId: userId,
202
+ projectName: projectName,
203
+ timestamp: new Date().toISOString(),
204
+ pageUrl: window.location.href,
205
+ referrer: referrer, // 这里的 referrer 是跳转前的页面 URL
206
+ pv: pv,
207
+ }, reportUrl);
208
+ };
209
+ window.addEventListener('hashchange', handleRouteChange);
210
+ window.addEventListener('popstate', handleRouteChange);
211
+ var originalPush = history.pushState;
212
+ var originalReplace = history.replaceState;
213
+ history.pushState = function () {
214
+ var args = [];
215
+ for (var _i = 0; _i < arguments.length; _i++) {
216
+ args[_i] = arguments[_i];
217
+ }
218
+ originalPush.apply(this, args);
219
+ handleRouteChange();
220
+ };
221
+ history.replaceState = function () {
222
+ var args = [];
223
+ for (var _i = 0; _i < arguments.length; _i++) {
224
+ args[_i] = arguments[_i];
225
+ }
226
+ originalReplace.apply(this, args);
227
+ handleRouteChange();
228
+ };
229
+ };
230
+
231
+ // 初始化用户行为监控​
232
+ var initUserBehaviorMonitor = function (_a) {
233
+ var projectName = _a.projectName, reportUrl = _a.reportUrl;
234
+ var userId = getUserID();
235
+ // UV 统计:如果是用户首次访问,记录 UV​
236
+ if (!isUVRecorded()) {
237
+ sendBehaviorData({
238
+ behavior: 'uv',
239
+ userId: userId,
240
+ projectName: projectName,
241
+ timestamp: new Date().toISOString(),
242
+ }, reportUrl);
243
+ setUVRecorded();
244
+ }
245
+ // 启动点击行为、PV 统计和页面停留时间监控​
246
+ trackUserBehavior(projectName, reportUrl);
247
+ };
248
+
249
+ export { initUserBehaviorMonitor };
250
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":["../src/sender.ts","../src/storage.ts","../src/tracker.ts","../src/index.ts"],"sourcesContent":["/**\r\n * 发送用户行为数据\r\n * @param data - 用户行为数据\r\n * @param url - 数据上报的URL\r\n */\r\nexport const sendBehaviorData = (data: Record<string, any>, url: string) => {\r\n if (navigator.sendBeacon) {\r\n const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });\r\n navigator.sendBeacon(url, blob);\r\n } else {\r\n fetch(url, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(data),\r\n }).catch((error) => console.error('Error sending behavior data:', error));\r\n }\r\n};","const USER_ID_KEY = 'user_behavior_user_id';\r\nconst PV_COUNT_KEY = 'user_behavior_pv_count';\r\nconst UV_STORAGE_KEY = 'user_behavior_uv';\r\n\r\n// 获取用户唯一标识(UUID)​\r\nexport const getUserID = (): string => {\r\n let userId = localStorage.getItem(USER_ID_KEY);\r\n if (!userId) {\r\n userId = generateUUID();\r\n localStorage.setItem(USER_ID_KEY, userId);\r\n }\r\n return userId;\r\n};\r\n\r\n/**\r\n * @description: 生成唯一标识符\r\n * @return {string} 唯一标识符\r\n */\r\nconst generateUUID = (): string => {\r\n return 'xxxx-xxxx-4xxx-yxxx-xxxx'.replace(/[xy]/g, (char) => {\r\n const random = (Math.random() * 16) | 0;\r\n const value = char === 'x' ? random : (random & 0x3) | 0x8;\r\n return value.toString(16);\r\n });\r\n};\r\n\r\n\r\nexport const incrementPV = (): number => {\r\n const today = new Date().toISOString().split('T')[0]; // 获取当天的日期​\r\n const pvData = localStorage.getItem(`${PV_COUNT_KEY}_${today}`);\r\n const newPV = (pvData ? parseInt(pvData, 10) : 0) + 1;\r\n localStorage.setItem(`${PV_COUNT_KEY}_${today}`, newPV.toString());\r\n return newPV;\r\n};\r\n\r\n\r\n// 检查是否已经记录 UV​\r\nexport const isUVRecorded = (): boolean => {\r\n const today = new Date().toISOString().split('T')[0];\r\n return localStorage.getItem(UV_STORAGE_KEY) === today;\r\n};\r\n\r\n\r\n// 设置 UV 记录​\r\nexport const setUVRecorded = () => {\r\n const today = new Date().toISOString().split('T')[0];\r\n localStorage.setItem(UV_STORAGE_KEY, today);\r\n};","import { sendBehaviorData } from './sender';\r\nimport { getUserID, incrementPV } from './storage';\r\n\r\n// 页面加载时间\r\nlet pageLoadTime: number = Date.now();\r\n// 上一个页面的 URL\r\nlet lastPageUrl: string = window.location.href;\r\nlet lastDwellReportedForLoadTime: number | null = null;\r\n\r\n\r\n/**\r\n * @description: 跟踪用户行为(用户点击、MPA 首次加载 PV、通用停留时间、SPA 路由行为)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n */\r\nexport const trackUserBehavior = (projectName: string, reportUrl: string) => {\r\n // 1. 通过事件委托捕获点击\r\n trackClicks(projectName, reportUrl);\r\n\r\n // 2. MPA 首次加载(页面初次进入)PV 上报\r\n trackMpaPageView(projectName, reportUrl);\r\n\r\n // 3. 通用停留时间(关闭页面/切换标签)上报\r\n trackPageDwellTime(projectName, reportUrl);\r\n\r\n // 4. SPA 路由行为(路由变化:先上报旧页面停留,再上报新页面 PV)\r\n trackSpaBehavior(projectName, reportUrl);\r\n};\r\n\r\n/**\r\n * @description: 上报页面停留时间\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst reportDwellTime = (projectName: string, reportUrl: string) => {\r\n if (lastDwellReportedForLoadTime === pageLoadTime) return;\r\n const dwellTime = Date.now() - pageLoadTime;\r\n if (dwellTime > 0) {\r\n sendBehaviorData({\r\n behavior: 'dwell',\r\n userId: getUserID(),\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n pageUrl: lastPageUrl,\r\n dwellTime,\r\n }, reportUrl);\r\n lastDwellReportedForLoadTime = pageLoadTime;\r\n }\r\n};\r\n\r\n\r\n/**\r\n * 捕获首屏 PV(MPA传统网页)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n */\r\nconst trackMpaPageView = (projectName: string, reportUrl: string) => {\r\n window.addEventListener('load', () => {\r\n const userId = getUserID();\r\n const pv = incrementPV(); // 增加 PV 计数\r\n\r\n // 发送 PV 数据\r\n sendBehaviorData({\r\n behavior: 'pv',\r\n userId,\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n pageUrl: window.location.href,\r\n referrer: document.referrer || '', // 记录来源\r\n pv,\r\n }, reportUrl);\r\n\r\n // 记录页面加载时间\r\n pageLoadTime = Date.now();\r\n lastPageUrl = window.location.href;\r\n });\r\n};\r\n\r\n\r\n/**\r\n * @description: 捕获点击事件,通过 data-track-click 属性简化识别​\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst trackClicks = (projectName: string, reportUrl: string) => {\r\n document.addEventListener('click', (event) => {\r\n const target = event.target as HTMLElement;\r\n\r\n // 如果目标元素带有 data-track-click 属性​\r\n if (target && target.dataset.trackClick) {\r\n const behaviorData = {\r\n behavior: 'click',\r\n userId: getUserID(),\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n element: target.tagName,\r\n action: target.dataset.trackClick, // 自定义的点击行为​\r\n pageUrl: window.location.href,\r\n referrer: lastPageUrl, // 记录点击时的页面来源\r\n };\r\n\r\n // 发送点击事件数据到服务端​\r\n sendBehaviorData(behaviorData, reportUrl);\r\n }\r\n });\r\n};\r\n\r\n\r\n\r\n/**\r\n * @description: 捕获页面停留时间(关闭/隐藏)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst trackPageDwellTime = (projectName: string, reportUrl: string) => {\r\n // 在 beforeunload 时计算停留时间\r\n window.addEventListener('beforeunload', () => {\r\n reportDwellTime(projectName, reportUrl);\r\n });\r\n\r\n window.addEventListener('pagehide', () => {\r\n reportDwellTime(projectName, reportUrl);\r\n });\r\n\r\n // 在 visibilitychange(切换标签)时计算停留时间\r\n document.addEventListener('visibilitychange', () => {\r\n if (document.visibilityState === 'hidden') {\r\n reportDwellTime(projectName, reportUrl);\r\n }\r\n });\r\n};\r\n\r\n/**\r\n * @description: 捕获 SPA 路由行为(路由变化:先上报旧页面停留,再上报新页面 PV)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst trackSpaBehavior = (projectName: string, reportUrl: string) => {\r\n /**\r\n * @description: 处理路由变化事件回调\r\n * @returns \r\n */\r\n const handleRouteChange = () => {\r\n // 1. 防抖校验:如果 URL 没变(比如 hashchange 和 popstate 同时触发),直接退出\r\n if (window.location.href === lastPageUrl) return;\r\n\r\n // 2. 结算上一页:上报前一个页面的停留时间\r\n reportDwellTime(projectName, reportUrl);\r\n\r\n // 3. 记录当前 URL 为 referrer (在更新 lastPageUrl 之前!)\r\n const referrer = lastPageUrl;\r\n\r\n // 4. 更新状态:保存当前 URL,为下一次跳转做准备\r\n pageLoadTime = Date.now();\r\n lastPageUrl = window.location.href;\r\n\r\n // 5. 记录新页面:上报 PV\r\n const userId = getUserID();\r\n const pv = incrementPV();\r\n sendBehaviorData({\r\n behavior: 'pv',\r\n userId,\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n pageUrl: window.location.href,\r\n referrer: referrer, // 这里的 referrer 是跳转前的页面 URL\r\n pv,\r\n }, reportUrl);\r\n };\r\n\r\n window.addEventListener('hashchange', handleRouteChange);\r\n window.addEventListener('popstate', handleRouteChange);\r\n\r\n const originalPush = history.pushState;\r\n const originalReplace = history.replaceState;\r\n\r\n history.pushState = function (...args: Parameters<typeof history.pushState>) {\r\n originalPush.apply(this, args);\r\n handleRouteChange();\r\n };\r\n\r\n history.replaceState = function (...args: Parameters<typeof history.replaceState>) {\r\n originalReplace.apply(this, args);\r\n handleRouteChange();\r\n };\r\n};","\r\nimport { trackUserBehavior } from './tracker';\r\nimport { getUserID, isUVRecorded, setUVRecorded } from './storage';\r\nimport { sendBehaviorData } from './sender';\r\n\r\ninterface MonitorConfig {\r\n projectName: string; // 当前项目名称,用于区分项目​\r\n reportUrl: string; // 上报服务器的地址​\r\n}\r\n\r\n// 初始化用户行为监控​\r\nexport const initUserBehaviorMonitor = ({ projectName, reportUrl }: MonitorConfig) => {\r\n const userId = getUserID();\r\n\r\n // UV 统计:如果是用户首次访问,记录 UV​\r\n if (!isUVRecorded()) {\r\n sendBehaviorData({\r\n behavior: 'uv',\r\n userId,\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n }, reportUrl);\r\n setUVRecorded();\r\n }\r\n\r\n // 启动点击行为、PV 统计和页面停留时间监控​\r\n trackUserBehavior(projectName, reportUrl);\r\n};​"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).frontendBehaviorMonitor={})}(this,function(e){"use strict";var t=function(e,t){if(navigator.sendBeacon){var n=new Blob([JSON.stringify(e)],{type:"application/json"});navigator.sendBeacon(t,n)}else fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e)}).catch(function(e){return console.error("Error sending behavior data:",e)})},n="user_behavior_user_id",o="user_behavior_pv_count",r="user_behavior_uv",a=function(){var e=localStorage.getItem(n);return e||(e=i(),localStorage.setItem(n,e)),e},i=function(){return"xxxx-xxxx-4xxx-yxxx-xxxx".replace(/[xy]/g,function(e){var t=16*Math.random()|0;return("x"===e?t:3&t|8).toString(16)})},c=function(){var e=(new Date).toISOString().split("T")[0],t=localStorage.getItem("".concat(o,"_").concat(e)),n=(t?parseInt(t,10):0)+1;return localStorage.setItem("".concat(o,"_").concat(e),n.toString()),n},d=Date.now(),s=window.location.href,l=null,f=function(e,n){if(l!==d){var o=Date.now()-d;o>0&&(t({behavior:"dwell",userId:a(),projectName:e,timestamp:(new Date).toISOString(),pageUrl:s,dwellTime:o},n),l=d)}},u=function(e,n){window.addEventListener("load",function(){var o=a(),r=c();t({behavior:"pv",userId:o,projectName:e,timestamp:(new Date).toISOString(),pageUrl:window.location.href,referrer:document.referrer||"",pv:r},n),d=Date.now(),s=window.location.href})},p=function(e,n){document.addEventListener("click",function(o){var r=o.target;if(r&&r.dataset.trackClick){var i={behavior:"click",userId:a(),projectName:e,timestamp:(new Date).toISOString(),element:r.tagName,action:r.dataset.trackClick,pageUrl:window.location.href,referrer:s};t(i,n)}})},v=function(e,t){window.addEventListener("beforeunload",function(){f(e,t)}),window.addEventListener("pagehide",function(){f(e,t)}),document.addEventListener("visibilitychange",function(){"hidden"===document.visibilityState&&f(e,t)})},h=function(e,n){var o=function(){if(window.location.href!==s){f(e,n);var o=s;d=Date.now(),s=window.location.href;var r=a(),i=c();t({behavior:"pv",userId:r,projectName:e,timestamp:(new Date).toISOString(),pageUrl:window.location.href,referrer:o,pv:i},n)}};window.addEventListener("hashchange",o),window.addEventListener("popstate",o);var r=history.pushState,i=history.replaceState;history.pushState=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];r.apply(this,e),o()},history.replaceState=function(){for(var e=[],t=0;t<arguments.length;t++)e[t]=arguments[t];i.apply(this,e),o()}};e.initUserBehaviorMonitor=function(e){var n,o=e.projectName,i=e.reportUrl,c=a();n=(new Date).toISOString().split("T")[0],localStorage.getItem(r)!==n&&(t({behavior:"uv",userId:c,projectName:o,timestamp:(new Date).toISOString()},i),function(){var e=(new Date).toISOString().split("T")[0];localStorage.setItem(r,e)}()),function(e,t){p(e,t),u(e,t),v(e,t),h(e,t)}(o,i)}});
2
+ //# sourceMappingURL=index.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.umd.js","sources":["../src/sender.ts","../src/storage.ts","../src/tracker.ts","../src/index.ts"],"sourcesContent":["/**\r\n * 发送用户行为数据\r\n * @param data - 用户行为数据\r\n * @param url - 数据上报的URL\r\n */\r\nexport const sendBehaviorData = (data: Record<string, any>, url: string) => {\r\n if (navigator.sendBeacon) {\r\n const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });\r\n navigator.sendBeacon(url, blob);\r\n } else {\r\n fetch(url, {\r\n method: 'POST',\r\n headers: { 'Content-Type': 'application/json' },\r\n body: JSON.stringify(data),\r\n }).catch((error) => console.error('Error sending behavior data:', error));\r\n }\r\n};","const USER_ID_KEY = 'user_behavior_user_id';\r\nconst PV_COUNT_KEY = 'user_behavior_pv_count';\r\nconst UV_STORAGE_KEY = 'user_behavior_uv';\r\n\r\n// 获取用户唯一标识(UUID)​\r\nexport const getUserID = (): string => {\r\n let userId = localStorage.getItem(USER_ID_KEY);\r\n if (!userId) {\r\n userId = generateUUID();\r\n localStorage.setItem(USER_ID_KEY, userId);\r\n }\r\n return userId;\r\n};\r\n\r\n/**\r\n * @description: 生成唯一标识符\r\n * @return {string} 唯一标识符\r\n */\r\nconst generateUUID = (): string => {\r\n return 'xxxx-xxxx-4xxx-yxxx-xxxx'.replace(/[xy]/g, (char) => {\r\n const random = (Math.random() * 16) | 0;\r\n const value = char === 'x' ? random : (random & 0x3) | 0x8;\r\n return value.toString(16);\r\n });\r\n};\r\n\r\n\r\nexport const incrementPV = (): number => {\r\n const today = new Date().toISOString().split('T')[0]; // 获取当天的日期​\r\n const pvData = localStorage.getItem(`${PV_COUNT_KEY}_${today}`);\r\n const newPV = (pvData ? parseInt(pvData, 10) : 0) + 1;\r\n localStorage.setItem(`${PV_COUNT_KEY}_${today}`, newPV.toString());\r\n return newPV;\r\n};\r\n\r\n\r\n// 检查是否已经记录 UV​\r\nexport const isUVRecorded = (): boolean => {\r\n const today = new Date().toISOString().split('T')[0];\r\n return localStorage.getItem(UV_STORAGE_KEY) === today;\r\n};\r\n\r\n\r\n// 设置 UV 记录​\r\nexport const setUVRecorded = () => {\r\n const today = new Date().toISOString().split('T')[0];\r\n localStorage.setItem(UV_STORAGE_KEY, today);\r\n};","import { sendBehaviorData } from './sender';\r\nimport { getUserID, incrementPV } from './storage';\r\n\r\n// 页面加载时间\r\nlet pageLoadTime: number = Date.now();\r\n// 上一个页面的 URL\r\nlet lastPageUrl: string = window.location.href;\r\nlet lastDwellReportedForLoadTime: number | null = null;\r\n\r\n\r\n/**\r\n * @description: 跟踪用户行为(用户点击、MPA 首次加载 PV、通用停留时间、SPA 路由行为)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n */\r\nexport const trackUserBehavior = (projectName: string, reportUrl: string) => {\r\n // 1. 通过事件委托捕获点击\r\n trackClicks(projectName, reportUrl);\r\n\r\n // 2. MPA 首次加载(页面初次进入)PV 上报\r\n trackMpaPageView(projectName, reportUrl);\r\n\r\n // 3. 通用停留时间(关闭页面/切换标签)上报\r\n trackPageDwellTime(projectName, reportUrl);\r\n\r\n // 4. SPA 路由行为(路由变化:先上报旧页面停留,再上报新页面 PV)\r\n trackSpaBehavior(projectName, reportUrl);\r\n};\r\n\r\n/**\r\n * @description: 上报页面停留时间\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst reportDwellTime = (projectName: string, reportUrl: string) => {\r\n if (lastDwellReportedForLoadTime === pageLoadTime) return;\r\n const dwellTime = Date.now() - pageLoadTime;\r\n if (dwellTime > 0) {\r\n sendBehaviorData({\r\n behavior: 'dwell',\r\n userId: getUserID(),\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n pageUrl: lastPageUrl,\r\n dwellTime,\r\n }, reportUrl);\r\n lastDwellReportedForLoadTime = pageLoadTime;\r\n }\r\n};\r\n\r\n\r\n/**\r\n * 捕获首屏 PV(MPA传统网页)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n */\r\nconst trackMpaPageView = (projectName: string, reportUrl: string) => {\r\n window.addEventListener('load', () => {\r\n const userId = getUserID();\r\n const pv = incrementPV(); // 增加 PV 计数\r\n\r\n // 发送 PV 数据\r\n sendBehaviorData({\r\n behavior: 'pv',\r\n userId,\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n pageUrl: window.location.href,\r\n referrer: document.referrer || '', // 记录来源\r\n pv,\r\n }, reportUrl);\r\n\r\n // 记录页面加载时间\r\n pageLoadTime = Date.now();\r\n lastPageUrl = window.location.href;\r\n });\r\n};\r\n\r\n\r\n/**\r\n * @description: 捕获点击事件,通过 data-track-click 属性简化识别​\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst trackClicks = (projectName: string, reportUrl: string) => {\r\n document.addEventListener('click', (event) => {\r\n const target = event.target as HTMLElement;\r\n\r\n // 如果目标元素带有 data-track-click 属性​\r\n if (target && target.dataset.trackClick) {\r\n const behaviorData = {\r\n behavior: 'click',\r\n userId: getUserID(),\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n element: target.tagName,\r\n action: target.dataset.trackClick, // 自定义的点击行为​\r\n pageUrl: window.location.href,\r\n referrer: lastPageUrl, // 记录点击时的页面来源\r\n };\r\n\r\n // 发送点击事件数据到服务端​\r\n sendBehaviorData(behaviorData, reportUrl);\r\n }\r\n });\r\n};\r\n\r\n\r\n\r\n/**\r\n * @description: 捕获页面停留时间(关闭/隐藏)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst trackPageDwellTime = (projectName: string, reportUrl: string) => {\r\n // 在 beforeunload 时计算停留时间\r\n window.addEventListener('beforeunload', () => {\r\n reportDwellTime(projectName, reportUrl);\r\n });\r\n\r\n window.addEventListener('pagehide', () => {\r\n reportDwellTime(projectName, reportUrl);\r\n });\r\n\r\n // 在 visibilitychange(切换标签)时计算停留时间\r\n document.addEventListener('visibilitychange', () => {\r\n if (document.visibilityState === 'hidden') {\r\n reportDwellTime(projectName, reportUrl);\r\n }\r\n });\r\n};\r\n\r\n/**\r\n * @description: 捕获 SPA 路由行为(路由变化:先上报旧页面停留,再上报新页面 PV)\r\n * @param projectName 项目名称\r\n * @param reportUrl 上报 URL\r\n * @returns \r\n */\r\nconst trackSpaBehavior = (projectName: string, reportUrl: string) => {\r\n /**\r\n * @description: 处理路由变化事件回调\r\n * @returns \r\n */\r\n const handleRouteChange = () => {\r\n // 1. 防抖校验:如果 URL 没变(比如 hashchange 和 popstate 同时触发),直接退出\r\n if (window.location.href === lastPageUrl) return;\r\n\r\n // 2. 结算上一页:上报前一个页面的停留时间\r\n reportDwellTime(projectName, reportUrl);\r\n\r\n // 3. 记录当前 URL 为 referrer (在更新 lastPageUrl 之前!)\r\n const referrer = lastPageUrl;\r\n\r\n // 4. 更新状态:保存当前 URL,为下一次跳转做准备\r\n pageLoadTime = Date.now();\r\n lastPageUrl = window.location.href;\r\n\r\n // 5. 记录新页面:上报 PV\r\n const userId = getUserID();\r\n const pv = incrementPV();\r\n sendBehaviorData({\r\n behavior: 'pv',\r\n userId,\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n pageUrl: window.location.href,\r\n referrer: referrer, // 这里的 referrer 是跳转前的页面 URL\r\n pv,\r\n }, reportUrl);\r\n };\r\n\r\n window.addEventListener('hashchange', handleRouteChange);\r\n window.addEventListener('popstate', handleRouteChange);\r\n\r\n const originalPush = history.pushState;\r\n const originalReplace = history.replaceState;\r\n\r\n history.pushState = function (...args: Parameters<typeof history.pushState>) {\r\n originalPush.apply(this, args);\r\n handleRouteChange();\r\n };\r\n\r\n history.replaceState = function (...args: Parameters<typeof history.replaceState>) {\r\n originalReplace.apply(this, args);\r\n handleRouteChange();\r\n };\r\n};","\r\nimport { trackUserBehavior } from './tracker';\r\nimport { getUserID, isUVRecorded, setUVRecorded } from './storage';\r\nimport { sendBehaviorData } from './sender';\r\n\r\ninterface MonitorConfig {\r\n projectName: string; // 当前项目名称,用于区分项目​\r\n reportUrl: string; // 上报服务器的地址​\r\n}\r\n\r\n// 初始化用户行为监控​\r\nexport const initUserBehaviorMonitor = ({ projectName, reportUrl }: MonitorConfig) => {\r\n const userId = getUserID();\r\n\r\n // UV 统计:如果是用户首次访问,记录 UV​\r\n if (!isUVRecorded()) {\r\n sendBehaviorData({\r\n behavior: 'uv',\r\n userId,\r\n projectName,\r\n timestamp: new Date().toISOString(),\r\n }, reportUrl);\r\n setUVRecorded();\r\n }\r\n\r\n // 启动点击行为、PV 统计和页面停留时间监控​\r\n trackUserBehavior(projectName, reportUrl);\r\n};​"],"names":["sendBehaviorData","data","url","navigator","sendBeacon","blob","Blob","JSON","stringify","type","fetch","method","headers","body","catch","error","console","USER_ID_KEY","PV_COUNT_KEY","UV_STORAGE_KEY","getUserID","userId","localStorage","getItem","generateUUID","setItem","replace","char","random","Math","toString","incrementPV","today","Date","toISOString","split","pvData","concat","newPV","parseInt","pageLoadTime","now","lastPageUrl","window","location","href","lastDwellReportedForLoadTime","reportDwellTime","projectName","reportUrl","dwellTime","behavior","timestamp","pageUrl","trackMpaPageView","addEventListener","pv","referrer","document","trackClicks","event","target","dataset","trackClick","behaviorData","element","tagName","action","trackPageDwellTime","visibilityState","trackSpaBehavior","handleRouteChange","originalPush","history","pushState","originalReplace","replaceState","args","_i","arguments","length","apply","this","_a","setUVRecorded","trackUserBehavior"],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 发送用户行为数据
3
+ * @param data - 用户行为数据
4
+ * @param url - 数据上报的URL
5
+ */
6
+ export declare const sendBehaviorData: (data: Record<string, any>, url: string) => void;
@@ -0,0 +1,4 @@
1
+ export declare const getUserID: () => string;
2
+ export declare const incrementPV: () => number;
3
+ export declare const isUVRecorded: () => boolean;
4
+ export declare const setUVRecorded: () => void;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * @description: 跟踪用户行为(用户点击、MPA 首次加载 PV、通用停留时间、SPA 路由行为)
3
+ * @param projectName 项目名称
4
+ * @param reportUrl 上报 URL
5
+ */
6
+ export declare const trackUserBehavior: (projectName: string, reportUrl: string) => void;
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "behavior-aliu",
3
+ "version": "1.0.0",
4
+ "description": "A lightweight front-end behavior monitoring SDK",
5
+ "main": "dist/index.cjs.js",
6
+ "module": "dist/index.esm.js",
7
+ "browser": "dist/index.umd.js",
8
+ "type": "module",
9
+ "scripts": {
10
+ "build": "rollup -c",
11
+ "dev": "rollup -c -w",
12
+ "demo":"node test/server.js"
13
+ },
14
+ "keywords": [
15
+ "behavior-monitor",
16
+ "frontend",
17
+ "sdk"
18
+ ],
19
+ "license": "MIT",
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "devDependencies": {
24
+ "@rollup/plugin-terser": "^0.4.0",
25
+ "@rollup/plugin-typescript": "^11.1.0",
26
+ "cors": "^2.8.5",
27
+ "express": "^5.2.1",
28
+ "rollup": "^4.9.0",
29
+ "tslib": "^2.6.0",
30
+ "typescript": "^5.3.0"
31
+ }
32
+ }