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