|
@@ -0,0 +1,79 @@
|
|
|
+class Tracker {
|
|
|
+ constructor(options = {}) {
|
|
|
+ this.baseUrl = options.baseUrl || '';
|
|
|
+ this.timer = null;
|
|
|
+ this.startTime = 0;
|
|
|
+ this.heartbeatInterval = options.heartbeatInterval || 30000; // 默认30秒发送一次心跳
|
|
|
+ }
|
|
|
+
|
|
|
+ init() {
|
|
|
+ this.startTime = Date.now();
|
|
|
+ this.bindEvents();
|
|
|
+ this.startHeartbeat();
|
|
|
+ }
|
|
|
+
|
|
|
+ bindEvents() {
|
|
|
+ window.addEventListener('beforeunload', this.handleBeforeUnload);
|
|
|
+ window.addEventListener('click', this.handleClickEvent);
|
|
|
+ }
|
|
|
+
|
|
|
+ startHeartbeat() {
|
|
|
+ this.timer = setInterval(() => {
|
|
|
+ this.sendData({
|
|
|
+ eventType: 'heartbeat',
|
|
|
+ duration: Math.round((Date.now() - this.startTime) / 1000),
|
|
|
+ }, {heartbeatSeconds: this.heartbeatInterval});
|
|
|
+ }, this.heartbeatInterval);
|
|
|
+ }
|
|
|
+
|
|
|
+ handleBeforeUnload = (e) => {
|
|
|
+ const data = {
|
|
|
+ eventType: 'page_close',
|
|
|
+ duration: Math.round((Date.now() - this.startTime) / 1000),
|
|
|
+ };
|
|
|
+ const payload = JSON.stringify(data);
|
|
|
+ if (navigator.sendBeacon) {
|
|
|
+ navigator.sendBeacon(this.baseUrl, payload);
|
|
|
+ } else {
|
|
|
+ this.sendData(payload, {heartbeatSeconds: this.heartbeatInterval});
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ handleClickEvent = (event) => {
|
|
|
+ const target = event.target;
|
|
|
+ if (target.matches('[data-track]')) {
|
|
|
+ const trackInfo = {
|
|
|
+ eventType: 'button_click',
|
|
|
+ elementId: target.id || '无ID',
|
|
|
+ elementClass: target.className || '无class',
|
|
|
+ text: target.innerText || target.textContent || '',
|
|
|
+ timestamp: new Date().toISOString(),
|
|
|
+ };
|
|
|
+ this.sendData(trackInfo, {heartbeatSeconds: this.heartbeatInterval});
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ sendData(data, headers = {}) {
|
|
|
+ console.log('上报埋点数据:', data);
|
|
|
+ fetch(`${this.baseUrl}`, { method: 'POST', headers: { 'Content-Type': 'application/json' ,...headers}, body: JSON.stringify(data) });
|
|
|
+ }
|
|
|
+
|
|
|
+ destroy() {
|
|
|
+ window.removeEventListener('beforeunload', this.handleBeforeUnload);
|
|
|
+ window.removeEventListener('click', this.handleClickEvent);
|
|
|
+ if (this.timer) {
|
|
|
+ clearInterval(this.timer);
|
|
|
+ }
|
|
|
+ this.sendData({
|
|
|
+ eventType: 'tracker_destroyed',
|
|
|
+ duration: Math.round((Date.now() - this.startTime) / 1000),
|
|
|
+ }, {heartbeatSeconds: this.heartbeatInterval});
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 使用示例:
|
|
|
+// const tracker = new Tracker({ baseUrl: 'https://your-tracking-api.com' });
|
|
|
+// tracker.init();
|
|
|
+
|
|
|
+// 暴露给全局或者作为模块导出
|
|
|
+window.Tracker = Tracker;
|