@fle-sdk/event-tracking-web 1.2.0 → 1.2.2

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/lib/index.esm.js CHANGED
@@ -1246,7 +1246,19 @@ function (_super) {
1246
1246
 
1247
1247
  _this.batchTimer = null; // LocalStorage 存储 key
1248
1248
 
1249
- _this.BATCH_QUEUE_STORAGE_KEY = 'web_tracking_batch_queue'; // 用户信息
1249
+ _this.BATCH_QUEUE_STORAGE_KEY = "web_tracking_batch_queue"; // 是否使用自定义 pageKey(如果为 true,路由变化时不会自动更新 pageKey)
1250
+
1251
+ _this.useCustomPageKey = false; // 待发送的单个请求队列(用于页面跳转时发送)
1252
+
1253
+ _this.pendingRequests = []; // 页面卸载监听器是否已设置
1254
+
1255
+ _this.isUnloadListenerSetup = false; // LocalStorage 存储 key(待发送请求)
1256
+
1257
+ _this.PENDING_REQUESTS_STORAGE_KEY = "web_tracking_pending_requests"; // 待发送请求队列最大大小(默认值,可通过配置覆盖)
1258
+
1259
+ _this.DEFAULT_PENDING_REQUESTS_MAX_SIZE = 50; // LocalStorage 最大大小限制(4MB)
1260
+
1261
+ _this.MAX_STORAGE_SIZE = 4 * 1024 * 1024; // 用户信息
1250
1262
 
1251
1263
  _this.userInfo = null; // 当前路由
1252
1264
 
@@ -1271,21 +1283,36 @@ function (_super) {
1271
1283
  _this.preset(initParams);
1272
1284
 
1273
1285
  var pathname = window.location.pathname;
1274
- _this.currentUrl = window.location.href;
1275
- _this.pageKey = pathname.replace(/\//g, "_").substring(1);
1286
+ _this.currentUrl = window.location.href; // 如果传入了自定义 pageKey,使用自定义值,否则自动生成
1287
+
1288
+ if (initParams.pageKey) {
1289
+ _this.pageKey = initParams.pageKey;
1290
+ _this.useCustomPageKey = true;
1291
+ } else {
1292
+ _this.pageKey = pathname.replace(/\//g, "_").substring(1);
1293
+ _this.useCustomPageKey = false;
1294
+ }
1295
+
1276
1296
  _this.systemsInfo = _this.getSystemsInfo(initParams.platform); // 获取设备ID
1277
1297
 
1278
- _this.deviceId = _this.getDeviceId();
1298
+ _this.deviceId = _this.getDeviceId(); // 如果传入了 userInfo,设置用户信息
1299
+
1300
+ if (initParams.userInfo && _this.isObject(initParams.userInfo)) {
1301
+ _this.userInfo = initParams.userInfo;
1302
+ }
1279
1303
 
1280
1304
  _this.setCookie("retainedStartTime", _this.getTimeStamp()); // 如果启用了批量发送,从 LocalStorage 恢复队列
1281
1305
 
1282
1306
 
1283
1307
  if (_this.initConfig.batchSend) {
1284
- _this.restoreBatchQueueFromStorage(); // 监听页面卸载事件,保存队列
1308
+ _this.restoreBatchQueueFromStorage();
1309
+ } // 恢复待发送的单个请求
1285
1310
 
1286
1311
 
1287
- _this.setupBeforeUnloadListener();
1288
- }
1312
+ _this.restorePendingRequestsFromStorage(); // 无论是否启用批量发送,都需要监听页面卸载事件,确保数据发送
1313
+
1314
+
1315
+ _this.setupBeforeUnloadListener();
1289
1316
  };
1290
1317
  /**
1291
1318
  * TODO: 需要判断有哪些不能被预制的参数
@@ -1296,7 +1323,23 @@ function (_super) {
1296
1323
 
1297
1324
  _this.preset = function (presetParams) {
1298
1325
  if (presetParams instanceof Object) {
1326
+ // 处理 pageKey 特殊逻辑
1327
+ if (presetParams.pageKey !== undefined) {
1328
+ if (presetParams.pageKey === null || presetParams.pageKey === '') {
1329
+ // 恢复自动生成
1330
+ _this.useCustomPageKey = false;
1331
+ var pathname = window.location.pathname;
1332
+ _this.pageKey = pathname.replace(/\//g, "_").substring(1);
1333
+ } else {
1334
+ _this.pageKey = presetParams.pageKey;
1335
+ _this.useCustomPageKey = true;
1336
+ }
1337
+ }
1338
+
1299
1339
  _this.each(presetParams, function (val, key) {
1340
+ // 跳过 pageKey,因为已经单独处理
1341
+ if (key === 'pageKey') return;
1342
+
1300
1343
  if (_this.initConfig.hasOwnProperty(key)) {
1301
1344
  // TODO:后面加一些校验
1302
1345
  _this.initConfig[key] = val;
@@ -1308,10 +1351,10 @@ function (_super) {
1308
1351
  _this.printLog("当前 server_url 为空或不正确,只在控制台打印日志,network 中不会发数据,请配置正确的 server_url!");
1309
1352
 
1310
1353
  _this.initConfig["showLog"] = true;
1311
- } // 如果启用了全埋点
1354
+ } // 如果启用了全埋点或启用了 data-part-key 点击追踪
1312
1355
 
1313
1356
 
1314
- if (!!_this.initConfig["autoTrack"]) {
1357
+ if (!!_this.initConfig["autoTrack"] || !!_this.initConfig["trackPartKeyClick"]) {
1315
1358
  // 启用监听
1316
1359
  _this.listener();
1317
1360
  } else {
@@ -1340,7 +1383,7 @@ function (_super) {
1340
1383
  } // 获取已存储的设备ID
1341
1384
 
1342
1385
 
1343
- var storedDeviceId = _this.getCookie('device_id') || _this.getLocalStorage('device_id');
1386
+ var storedDeviceId = _this.getCookie("device_id") || _this.getLocalStorage("device_id");
1344
1387
 
1345
1388
  if (storedDeviceId) {
1346
1389
  _this.deviceId = storedDeviceId;
@@ -1354,10 +1397,10 @@ function (_super) {
1354
1397
  var deviceId = _this.hashFingerprint(fingerprint); // 存储设备ID(统一使用2年过期时间,与tools.ts保持一致)
1355
1398
 
1356
1399
 
1357
- _this.setCookie('device_id', deviceId, 365 * 2); // 存储2年
1400
+ _this.setCookie("device_id", deviceId, 365 * 2); // 存储2年
1358
1401
 
1359
1402
 
1360
- _this.setLocalStorage('device_id', deviceId);
1403
+ _this.setLocalStorage("device_id", deviceId);
1361
1404
 
1362
1405
  _this.deviceId = deviceId;
1363
1406
  return _this.deviceId;
@@ -1371,10 +1414,10 @@ function (_super) {
1371
1414
 
1372
1415
  _this.resetDeviceId = function () {
1373
1416
  // 清除cookie和localStorage中的设备ID
1374
- document.cookie = 'device_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
1375
- localStorage.removeItem('device_id'); // 清除内存中的设备ID
1417
+ document.cookie = "device_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
1418
+ localStorage.removeItem("device_id"); // 清除内存中的设备ID
1376
1419
 
1377
- _this.deviceId = ''; // 重新生成设备ID
1420
+ _this.deviceId = ""; // 重新生成设备ID
1378
1421
 
1379
1422
  var newDeviceId = _this.getDeviceId();
1380
1423
 
@@ -1389,6 +1432,7 @@ function (_super) {
1389
1432
 
1390
1433
  _this.track = function (_a) {
1391
1434
  var desc = _a.desc,
1435
+ pageKey = _a.pageKey,
1392
1436
  partkey = _a.partkey,
1393
1437
  business = _a.business,
1394
1438
  header = _a.header;
@@ -1396,7 +1440,7 @@ function (_super) {
1396
1440
  var params = _this.getParams({
1397
1441
  desc: desc,
1398
1442
  event: "CustomTrack",
1399
- itemKey: _this.getItemKey(partkey),
1443
+ itemKey: _this.getItemKey(partkey, pageKey),
1400
1444
  privateParamMap: {
1401
1445
  business: business
1402
1446
  }
@@ -1410,17 +1454,23 @@ function (_super) {
1410
1454
 
1411
1455
 
1412
1456
  _this.listener = function () {
1413
- if (!!_this.initConfig.isTrackSinglePage) {
1414
- _this.rewriteHistory();
1457
+ // 如果启用了全埋点,监听页面浏览事件
1458
+ if (!!_this.initConfig.autoTrack) {
1459
+ if (!!_this.initConfig.isTrackSinglePage) {
1460
+ _this.rewriteHistory();
1415
1461
 
1416
- _this.addSinglePageEvent(_this.onPageViewCallback);
1417
- }
1462
+ _this.addSinglePageEvent(_this.onPageViewCallback);
1463
+ }
1464
+
1465
+ _this.each(["load", "beforeunload"], function (historyType) {
1466
+ _this.addEventListener(window, historyType, _this.onPageViewCallback);
1467
+ });
1468
+ } // 如果启用了全埋点或启用了 data-part-key 点击追踪,监听点击事件
1418
1469
 
1419
- _this.each(["load", "beforeunload"], function (historyType) {
1420
- _this.addEventListener(window, historyType, _this.onPageViewCallback);
1421
- });
1422
1470
 
1423
- _this.addEventListener(window, "click", _this.onClickCallback);
1471
+ if (!!_this.initConfig.autoTrack || !!_this.initConfig.trackPartKeyClick) {
1472
+ _this.addEventListener(window, "click", _this.onClickCallback);
1473
+ }
1424
1474
  };
1425
1475
  /**
1426
1476
  * @description 取消全埋点事件
@@ -1465,12 +1515,51 @@ function (_super) {
1465
1515
  _this.clearBatchQueue = function () {
1466
1516
  _this.batchQueue = [];
1467
1517
 
1468
- _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, '[]');
1518
+ _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, "[]");
1469
1519
 
1470
1520
  if (_this.initConfig.showLog) {
1471
- _this.printLog('批量队列已清空');
1521
+ _this.printLog("批量队列已清空");
1522
+ }
1523
+ };
1524
+ /**
1525
+ * @description 设置自定义页面唯一标识
1526
+ * @param pageKey 页面唯一标识,如果传入 null 或空字符串,则恢复自动生成
1527
+ * @param autoUpdate 路由变化时是否自动更新(默认:false,使用自定义值后不再自动更新)
1528
+ */
1529
+
1530
+
1531
+ _this.setPageKey = function (pageKey, autoUpdate) {
1532
+ if (autoUpdate === void 0) {
1533
+ autoUpdate = false;
1534
+ }
1535
+
1536
+ if (pageKey === null || pageKey === '') {
1537
+ // 恢复自动生成
1538
+ _this.useCustomPageKey = false;
1539
+ var pathname = window.location.pathname;
1540
+ _this.pageKey = pathname.replace(/\//g, "_").substring(1);
1541
+
1542
+ if (_this.initConfig.showLog) {
1543
+ _this.printLog("\u9875\u9762\u6807\u8BC6\u5DF2\u6062\u590D\u81EA\u52A8\u751F\u6210: " + _this.pageKey);
1544
+ }
1545
+ } else {
1546
+ _this.pageKey = pageKey;
1547
+ _this.useCustomPageKey = !autoUpdate;
1548
+
1549
+ if (_this.initConfig.showLog) {
1550
+ _this.printLog("\u9875\u9762\u6807\u8BC6\u5DF2\u8BBE\u7F6E\u4E3A: " + pageKey + ", \u81EA\u52A8\u66F4\u65B0: " + autoUpdate);
1551
+ }
1472
1552
  }
1473
1553
  };
1554
+ /**
1555
+ * @description 获取当前页面唯一标识
1556
+ * @returns 当前页面唯一标识
1557
+ */
1558
+
1559
+
1560
+ _this.getPageKey = function () {
1561
+ return _this.pageKey;
1562
+ };
1474
1563
 
1475
1564
  _this.onClickCallback = function (e) {
1476
1565
  var _a;
@@ -1496,7 +1585,7 @@ function (_super) {
1496
1585
  targetEle: targetEle,
1497
1586
  pointerType: e.pointerType,
1498
1587
  currentUrl: _this.currentUrl,
1499
- elementSelector: _this.getDomSelector(target) || ''
1588
+ elementSelector: _this.getDomSelector(target) || ""
1500
1589
  }
1501
1590
  });
1502
1591
 
@@ -1508,7 +1597,12 @@ function (_super) {
1508
1597
 
1509
1598
 
1510
1599
  _this.onPageViewCallback = function (e) {
1511
- var _a, _b;
1600
+ var _a, _b; // 在路由变化前,先发送待发送的数据(避免被取消)
1601
+
1602
+
1603
+ if (_this.pendingRequests.length > 0 || _this.initConfig.batchSend && _this.batchQueue.length > 0) {
1604
+ _this.flushPendingData();
1605
+ }
1512
1606
 
1513
1607
  var ORGIN = window.location.origin;
1514
1608
 
@@ -1521,8 +1615,11 @@ function (_super) {
1521
1615
  }
1522
1616
  });
1523
1617
 
1524
- _this.currentUrl = window.location.href;
1525
- _this.pageKey = window.location.pathname.replace(/\//g, "_").substring(1);
1618
+ _this.currentUrl = window.location.href; // 如果使用自定义 pageKey,路由变化时不自动更新
1619
+
1620
+ if (!_this.useCustomPageKey) {
1621
+ _this.pageKey = window.location.pathname.replace(/\//g, "_").substring(1);
1622
+ }
1526
1623
 
1527
1624
  _this.sendRetained(e.type);
1528
1625
 
@@ -1753,9 +1850,121 @@ function (_super) {
1753
1850
  _this.printLog("\u6062\u590D\u6279\u91CF\u961F\u5217\u5931\u8D25: " + e); // 如果解析失败,清除损坏的数据
1754
1851
 
1755
1852
 
1756
- _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, '[]');
1853
+ _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, "[]");
1854
+ }
1855
+ };
1856
+ /**
1857
+ * 添加到待发送请求队列
1858
+ * @param params 数据参数
1859
+ */
1860
+
1861
+
1862
+ _this.addToPendingRequests = function (params) {
1863
+ _this.pendingRequests.push(params); // 限制队列大小,防止内存溢出
1864
+
1865
+
1866
+ var maxSize = _this.initConfig.pendingRequestsMaxSize || _this.DEFAULT_PENDING_REQUESTS_MAX_SIZE;
1867
+
1868
+ if (_this.pendingRequests.length > maxSize) {
1869
+ _this.pendingRequests = _this.pendingRequests.slice(-maxSize);
1870
+
1871
+ if (_this.initConfig.showLog) {
1872
+ _this.printLog("\u5F85\u53D1\u9001\u8BF7\u6C42\u961F\u5217\u5DF2\u6EE1\uFF0C\u5DF2\u79FB\u9664\u6700\u65E7\u7684\u6570\u636E\uFF08\u6700\u5927\u9650\u5236: " + maxSize + "\uFF09");
1873
+ }
1757
1874
  }
1758
1875
  };
1876
+ /**
1877
+ * 从 LocalStorage 恢复待发送请求
1878
+ */
1879
+
1880
+
1881
+ _this.restorePendingRequestsFromStorage = function () {
1882
+ try {
1883
+ var storedRequests = _this.getLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY);
1884
+
1885
+ if (storedRequests) {
1886
+ var parsedRequests = JSON.parse(storedRequests);
1887
+
1888
+ if (Array.isArray(parsedRequests) && parsedRequests.length > 0) {
1889
+ _this.pendingRequests = parsedRequests;
1890
+
1891
+ if (_this.initConfig.showLog) {
1892
+ _this.printLog("\u4ECE LocalStorage \u6062\u590D " + parsedRequests.length + " \u6761\u5F85\u53D1\u9001\u8BF7\u6C42");
1893
+ } // 恢复后立即尝试发送
1894
+
1895
+
1896
+ if (_this.pendingRequests.length > 0) {
1897
+ _this.flushPendingRequests();
1898
+ }
1899
+ }
1900
+ }
1901
+ } catch (e) {
1902
+ _this.printLog("\u6062\u590D\u5F85\u53D1\u9001\u8BF7\u6C42\u5931\u8D25: " + e); // 如果解析失败,清除损坏的数据
1903
+
1904
+
1905
+ _this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, "[]");
1906
+ }
1907
+ };
1908
+ /**
1909
+ * 检查页面是否即将卸载
1910
+ * @returns 如果页面即将卸载返回 true,否则返回 false
1911
+ */
1912
+
1913
+
1914
+ _this.isPageUnloading = function () {
1915
+ return document.visibilityState === "hidden";
1916
+ };
1917
+ /**
1918
+ * 使用 sendBeacon 发送数据(页面卸载时的备用方案)
1919
+ * @param params 数据参数
1920
+ * @param serverUrl 服务器地址
1921
+ * @param contentType 内容类型
1922
+ * @returns 是否发送成功
1923
+ */
1924
+
1925
+
1926
+ _this.sendWithBeacon = function (params, serverUrl, contentType) {
1927
+ try {
1928
+ var blob = new Blob([JSON.stringify(params)], {
1929
+ type: contentType || "application/json"
1930
+ });
1931
+ return navigator.sendBeacon(serverUrl, blob);
1932
+ } catch (e) {
1933
+ if (_this.initConfig.showLog) {
1934
+ _this.printLog("sendBeacon \u53D1\u9001\u5931\u8D25: " + e);
1935
+ }
1936
+
1937
+ return false;
1938
+ }
1939
+ };
1940
+ /**
1941
+ * 刷新待发送的单个请求(正常情况下的发送)
1942
+ * 注意:这个方法会直接发送,不会再次添加到 pendingRequests,避免循环
1943
+ */
1944
+
1945
+
1946
+ _this.flushPendingRequests = function () {
1947
+ if (_this.pendingRequests.length === 0) {
1948
+ return;
1949
+ }
1950
+
1951
+ var requestsToSend = __spreadArray([], _this.pendingRequests);
1952
+
1953
+ _this.pendingRequests = []; // 清除 LocalStorage 中的待发送请求
1954
+
1955
+ _this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, "[]"); // 尝试发送每个请求(使用 sendData,但如果失败不再添加到 pendingRequests,避免循环)
1956
+
1957
+
1958
+ requestsToSend.forEach(function (params) {
1959
+ // 直接调用 sendData,但不处理失败情况(避免循环)
1960
+ // 如果失败,数据会丢失,但这是可接受的,因为我们已经尝试过了
1961
+ _this.sendData(params).catch(function (err) {
1962
+ if (_this.initConfig.showLog) {
1963
+ _this.printLog("\u5F85\u53D1\u9001\u8BF7\u6C42\u53D1\u9001\u5931\u8D25\uFF08\u4E0D\u518D\u91CD\u8BD5\uFF09: " + err);
1964
+ }
1965
+ });
1966
+ });
1967
+ };
1759
1968
  /**
1760
1969
  * 保存批量队列到 LocalStorage
1761
1970
  */
@@ -1766,13 +1975,12 @@ function (_super) {
1766
1975
  var queueString = JSON.stringify(_this.batchQueue); // 检查存储大小,避免超出 LocalStorage 限制(通常 5-10MB)
1767
1976
  // 如果队列过大,只保留最新的数据
1768
1977
 
1769
- if (queueString.length > 4 * 1024 * 1024) {
1770
- // 4MB 限制
1978
+ if (queueString.length > _this.MAX_STORAGE_SIZE) {
1771
1979
  var maxItems = Math.floor(_this.batchQueue.length * 0.8); // 保留 80%
1772
1980
 
1773
1981
  _this.batchQueue = _this.batchQueue.slice(-maxItems);
1774
1982
 
1775
- _this.printLog("\u961F\u5217\u8FC7\u5927\uFF0C\u5DF2\u622A\u65AD\u4FDD\u7559\u6700\u65B0 " + maxItems + " \u6761\u6570\u636E");
1983
+ _this.printLog("\u961F\u5217\u8FC7\u5927\uFF0C\u5DF2\u622A\u65AD\u4FDD\u7559\u6700\u65B0 " + maxItems + " \u6761\u6570\u636E\uFF08\u9650\u5236: " + _this.MAX_STORAGE_SIZE / 1024 / 1024 + "MB\uFF09");
1776
1984
  }
1777
1985
 
1778
1986
  _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, JSON.stringify(_this.batchQueue));
@@ -1782,44 +1990,126 @@ function (_super) {
1782
1990
  }
1783
1991
  };
1784
1992
  /**
1785
- * 设置页面卸载监听器,保存队列
1993
+ * 设置页面卸载监听器,确保数据发送
1786
1994
  */
1787
1995
 
1788
1996
 
1789
1997
  _this.setupBeforeUnloadListener = function () {
1790
- // 使用 visibilitychange 事件(更可靠)
1791
- document.addEventListener('visibilitychange', function () {
1792
- if (document.visibilityState === 'hidden' && _this.batchQueue.length > 0) {
1793
- _this.saveBatchQueueToStorage();
1998
+ // 避免重复设置监听器
1999
+ if (_this.isUnloadListenerSetup) {
2000
+ return;
2001
+ }
2002
+
2003
+ _this.isUnloadListenerSetup = true; // 使用 visibilitychange 事件(更可靠,支持页面跳转、切换标签页等场景)
2004
+
2005
+ document.addEventListener("visibilitychange", function () {
2006
+ if (_this.isPageUnloading()) {
2007
+ _this.flushPendingData();
1794
2008
  }
1795
- }); // 使用 beforeunload 事件作为备用
2009
+ }); // 使用 beforeunload 事件作为备用(页面关闭/刷新)
1796
2010
 
1797
- window.addEventListener('beforeunload', function () {
1798
- if (_this.batchQueue.length > 0) {
1799
- // 使用 sendBeacon 尝试发送数据(如果支持)
1800
- if (navigator.sendBeacon && _this.initConfig.serverUrl) {
1801
- try {
1802
- var data = JSON.stringify({
1803
- events: _this.batchQueue
1804
- });
1805
- var blob = new Blob([data], {
1806
- type: 'application/json'
1807
- });
1808
- navigator.sendBeacon(_this.initConfig.serverUrl, blob); // 如果 sendBeacon 成功,清除队列
2011
+ window.addEventListener("beforeunload", function () {
2012
+ _this.flushPendingData();
2013
+ }); // 使用 pagehide 事件(更可靠,支持移动端)
1809
2014
 
1810
- _this.batchQueue = [];
2015
+ window.addEventListener("pagehide", function () {
2016
+ _this.flushPendingData();
2017
+ });
2018
+ };
2019
+ /**
2020
+ * 刷新待发送数据(在页面卸载/跳转时调用)
2021
+ */
1811
2022
 
1812
- _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, '[]');
1813
- } catch (e) {
1814
- // sendBeacon 失败,保存到 LocalStorage
1815
- _this.saveBatchQueueToStorage();
2023
+
2024
+ _this.flushPendingData = function () {
2025
+ // 收集所有待发送的数据
2026
+ var allPendingData = []; // 如果有批量队列,添加到待发送列表
2027
+
2028
+ if (_this.batchQueue.length > 0) {
2029
+ allPendingData.push.apply(allPendingData, _this.batchQueue);
2030
+ } // 如果有待发送的单个请求,也添加到列表
2031
+
2032
+
2033
+ if (_this.pendingRequests.length > 0) {
2034
+ allPendingData.push.apply(allPendingData, _this.pendingRequests);
2035
+ }
2036
+
2037
+ if (allPendingData.length === 0) {
2038
+ return;
2039
+ } // 使用 sendBeacon 发送数据(最可靠的方式)
2040
+
2041
+
2042
+ if (navigator.sendBeacon && _this.initConfig.serverUrl) {
2043
+ try {
2044
+ // 如果只有一条数据,直接发送;否则批量发送
2045
+ var dataToSend = allPendingData.length === 1 ? allPendingData[0] : {
2046
+ events: allPendingData
2047
+ };
2048
+ var blob = new Blob([JSON.stringify(dataToSend)], {
2049
+ type: _this.initConfig.contentType || "application/json"
2050
+ });
2051
+ var sent = navigator.sendBeacon(_this.initConfig.serverUrl, blob);
2052
+
2053
+ if (sent) {
2054
+ // 发送成功,清除所有队列
2055
+ _this.batchQueue = [];
2056
+ _this.pendingRequests = [];
2057
+
2058
+ _this.setLocalStorage(_this.BATCH_QUEUE_STORAGE_KEY, "[]");
2059
+
2060
+ if (_this.initConfig.showLog) {
2061
+ _this.printLog("\u9875\u9762\u5378\u8F7D\u65F6\u6210\u529F\u53D1\u9001 " + allPendingData.length + " \u6761\u6570\u636E");
1816
2062
  }
1817
2063
  } else {
1818
- // 不支持 sendBeacon,保存到 LocalStorage
2064
+ // sendBeacon 返回 false,保存到 LocalStorage(批量模式)
2065
+ if (_this.initConfig.batchSend && _this.batchQueue.length > 0) {
2066
+ _this.saveBatchQueueToStorage();
2067
+ } // 保存 pendingRequests 到 LocalStorage(如果支持)
2068
+
2069
+
2070
+ if (_this.pendingRequests.length > 0) {
2071
+ try {
2072
+ _this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, JSON.stringify(_this.pendingRequests));
2073
+ } catch (e) {
2074
+ // LocalStorage 可能已满或不可用
2075
+ if (_this.initConfig.showLog) {
2076
+ _this.printLog("\u4FDD\u5B58\u5F85\u53D1\u9001\u8BF7\u6C42\u5230 LocalStorage \u5931\u8D25: " + e);
2077
+ }
2078
+ }
2079
+ }
2080
+ }
2081
+ } catch (e) {
2082
+ // sendBeacon 失败,保存到 LocalStorage(批量模式)
2083
+ if (_this.initConfig.batchSend && _this.batchQueue.length > 0) {
1819
2084
  _this.saveBatchQueueToStorage();
2085
+ } // 保存 pendingRequests 到 LocalStorage(如果支持)
2086
+
2087
+
2088
+ if (_this.pendingRequests.length > 0) {
2089
+ try {
2090
+ _this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, JSON.stringify(_this.pendingRequests));
2091
+ } catch (e) {// LocalStorage 可能已满或不可用
2092
+ }
2093
+ }
2094
+
2095
+ if (_this.initConfig.showLog) {
2096
+ _this.printLog("\u9875\u9762\u5378\u8F7D\u65F6\u53D1\u9001\u6570\u636E\u5931\u8D25: " + e);
1820
2097
  }
1821
2098
  }
1822
- });
2099
+ } else {
2100
+ // 不支持 sendBeacon,保存到 LocalStorage(批量模式)
2101
+ if (_this.initConfig.batchSend && _this.batchQueue.length > 0) {
2102
+ _this.saveBatchQueueToStorage();
2103
+ } // 保存 pendingRequests 到 LocalStorage(如果支持)
2104
+
2105
+
2106
+ if (_this.pendingRequests.length > 0) {
2107
+ try {
2108
+ _this.setLocalStorage(_this.PENDING_REQUESTS_STORAGE_KEY, JSON.stringify(_this.pendingRequests));
2109
+ } catch (e) {// LocalStorage 可能已满或不可用
2110
+ }
2111
+ }
2112
+ }
1823
2113
  };
1824
2114
  /**
1825
2115
  * 发送数据通用函数
@@ -1851,16 +2141,61 @@ function (_super) {
1851
2141
  success: true,
1852
2142
  message: "已添加到批量队列"
1853
2143
  });
1854
- }
2144
+ } // 如果使用 sendBeacon 且没有自定义 header
2145
+
1855
2146
 
1856
2147
  if (_this.isSupportBeaconSend() === true && !header && !initHeader) {
2148
+ // 检查页面是否即将卸载,如果是,直接使用 sendBeacon 发送,避免被取消
2149
+ if (_this.isPageUnloading()) {
2150
+ var sent = _this.sendWithBeacon(params, serverUrl, contentType);
2151
+
2152
+ if (sent) {
2153
+ return Promise.resolve({
2154
+ success: true,
2155
+ message: "页面卸载时发送成功"
2156
+ });
2157
+ } else {
2158
+ // sendBeacon 返回 false,添加到待发送队列
2159
+ _this.addToPendingRequests(params);
2160
+
2161
+ return Promise.resolve({
2162
+ success: true,
2163
+ message: "已添加到待发送队列"
2164
+ });
2165
+ }
2166
+ } // 正常情况使用 sendBeacon
2167
+
2168
+
1857
2169
  return _this.sendBeacon({
1858
2170
  contentType: contentType,
1859
2171
  url: serverUrl,
1860
2172
  data: params
2173
+ }).catch(function (err) {
2174
+ // sendBeacon 失败,添加到待发送队列,避免数据丢失
2175
+ _this.addToPendingRequests(params);
2176
+
2177
+ return Promise.resolve({
2178
+ success: true,
2179
+ message: "sendBeacon 失败,已添加到待发送队列"
2180
+ });
1861
2181
  });
1862
2182
  } else {
2183
+ // 使用 XMLHttpRequest 发送
1863
2184
  return new Promise(function (resolve, reject) {
2185
+ // 如果页面即将卸载,也尝试使用 sendBeacon 作为备用
2186
+ if (_this.isPageUnloading() && _this.isSupportBeaconSend() && !header && !initHeader) {
2187
+ var sent = _this.sendWithBeacon(params, serverUrl, contentType);
2188
+
2189
+ if (sent) {
2190
+ resolve({
2191
+ success: true,
2192
+ message: "页面卸载时使用 sendBeacon 发送成功"
2193
+ });
2194
+ return;
2195
+ } // sendBeacon 失败,继续使用 XMLHttpRequest
2196
+
2197
+ }
2198
+
1864
2199
  _this.ajax({
1865
2200
  header: header || initHeader,
1866
2201
  url: serverUrl,
@@ -1877,7 +2212,20 @@ function (_super) {
1877
2212
  });
1878
2213
  },
1879
2214
  error: function error(err, status) {
1880
- return reject({
2215
+ // 如果请求失败且页面即将卸载,尝试使用 sendBeacon
2216
+ if (_this.isPageUnloading() && _this.isSupportBeaconSend() && !header && !initHeader) {
2217
+ var sent = _this.sendWithBeacon(params, serverUrl, contentType);
2218
+
2219
+ if (sent) {
2220
+ resolve({
2221
+ success: true,
2222
+ message: "XMLHttpRequest 失败,已使用 sendBeacon 发送"
2223
+ });
2224
+ return;
2225
+ }
2226
+ }
2227
+
2228
+ reject({
1881
2229
  success: false,
1882
2230
  message: String(err),
1883
2231
  code: status
@@ -1922,9 +2270,9 @@ function (_super) {
1922
2270
  */
1923
2271
 
1924
2272
 
1925
- _this.getItemKey = function (partkey) {
2273
+ _this.getItemKey = function (partkey, pageKey) {
1926
2274
  var appKey = _this.initConfig.appKey;
1927
- var keys = [appKey, _this.pageKey, partkey ? partkey.toString() : undefined].filter(function (key) {
2275
+ var keys = [appKey, (pageKey || _this.pageKey).toString(), partkey ? partkey.toString() : undefined].filter(function (key) {
1928
2276
  return !!key;
1929
2277
  });
1930
2278
  return keys.reduce(function (str, key) {
@@ -1948,7 +2296,9 @@ function (_super) {
1948
2296
  sampleRate: 1,
1949
2297
  batchSend: false,
1950
2298
  batchInterval: 5000,
1951
- batchMaxSize: 10 // 批量发送最大数量
2299
+ batchMaxSize: 10,
2300
+ trackPartKeyClick: false,
2301
+ pendingRequestsMaxSize: 50 // 待发送请求队列最大数量(防止内存溢出)
1952
2302
 
1953
2303
  }; // 系统信息
1954
2304