@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/README.md +148 -1
- package/lib/index.esm.js +414 -64
- package/lib/index.esm.min.js +1 -1
- package/lib/index.js +414 -64
- package/lib/index.min.js +1 -1
- package/lib/types/index.d.ts +52 -4
- package/lib/types/type.d.ts +23 -0
- package/package.json +1 -1
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
1400
|
+
_this.setCookie("device_id", deviceId, 365 * 2); // 存储2年
|
|
1358
1401
|
|
|
1359
1402
|
|
|
1360
|
-
_this.setLocalStorage(
|
|
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 =
|
|
1375
|
-
localStorage.removeItem(
|
|
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 =
|
|
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
|
-
|
|
1414
|
-
|
|
1457
|
+
// 如果启用了全埋点,监听页面浏览事件
|
|
1458
|
+
if (!!_this.initConfig.autoTrack) {
|
|
1459
|
+
if (!!_this.initConfig.isTrackSinglePage) {
|
|
1460
|
+
_this.rewriteHistory();
|
|
1415
1461
|
|
|
1416
|
-
|
|
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.
|
|
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
|
-
|
|
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 >
|
|
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
|
-
//
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
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(
|
|
1798
|
-
|
|
1799
|
-
|
|
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
|
-
|
|
2015
|
+
window.addEventListener("pagehide", function () {
|
|
2016
|
+
_this.flushPendingData();
|
|
2017
|
+
});
|
|
2018
|
+
};
|
|
2019
|
+
/**
|
|
2020
|
+
* 刷新待发送数据(在页面卸载/跳转时调用)
|
|
2021
|
+
*/
|
|
1811
2022
|
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
|