xb18
xb18
文章39
标签0
分类0
前端埋点

前端埋点

页面销毁

数据上报时机:

1
2
3
4
5
6
7
8
9
10
11
12
13
const onBeforeunload = async () => {
// 发起请求
}
window.addEventListener('beforeunload', onBeforeunload);

// 关闭页面时显示【离开此网站?】
window.addEventListener('beforeunload', (event) => {
// 阻止浏览器默认事件,也就是阻止关闭和刷新页面
event.preventDefault();
// chrome浏览器需要设置返回值
event.returnValue = '';
});

注意:在移动设备下,一些浏览器并不支持 beforeunload 事件,最可靠的方式是在 visibilitychange 事件中处理。

1
2
3
4
5
document.addEventListener('visibilitychange', function logData() {
if (document.visibilityState === 'hidden') {
...
}
});

避免使用 unload 和 beforeunload

页面关闭应该可以细分为两类:
1、目前浏览器打开了多个tab页面,只是关闭浏览器的一个tab页面,浏览器进程或主界面还在
2、目前浏览器就打开一个tab页面,关闭页面也就是关闭了浏览器进程【此时无法发送成功】

推荐使用fetch、sendBeacon上报

数据上报

  1. ajax(XMLHttpRequest)
  2. sendBeacon(Navigator.sendBeacon)
  3. fetch(Fetch keepalive)

ajax

对于 ajax 发起异步请求,若在发送过程中 刷新或关闭 浏览器,请求会被自动终止

如果想在控制台查看刷新前页面接口调用情况,可勾选 Preserve log 选项,Network 会保留上个页面的请求记录。

一般使用异步请求上报,目前,谷歌浏览器已经不允许在页面关闭期间发起 同步 XHR 请求,建议使用 sendBeacon 或者 fetch keep-alive

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
const ajax = (config) => {
const options = Object.assign({
url: '',
method: 'GET',
headers: {},
success: function () { },
error: function () { },
data: null,
timeout: 0,
async: true, // 是否异步发送请求,默认 true 是异步,同步需设置 false。
}, config);
const method = options.method.toUpperCase();

// 1、创建 xhr 对象
const xhr = new XMLHttpRequest();
xhr.timeout = options.timeout; // 设置请求超时时间

// 2、建立连接
xhr.open(method, options.url, options.async); // 第三参数决定是以 异步/同步 方式发起 HTTP 请求

// 设置请求头
Object.keys(options.headers).forEach(key => {
xhr.setRequestHeader(key, options.headers[key]);
});

// 3. 发送数据
xhr.send(['POST', 'PUT'].indexOf(method) > -1 ? JSON.stringify(options.data) : null);

// 4. 接收数据
xhr.onreadystatechange = function () { // 处理响应
if (xhr.readyState === 4) {
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 304) {
options.success(xhr.responseText);
} else {
options.error(xhr.status, xhr.statusText);
}
}
};

// 超时处理
xhr.ontimeout = function () { options.error(0, 'timeout') };
// 错误处理
xhr.onerror = function () { options.error(0, 'error') };
// xhr.abort(); // 取消请求
}

sendBeacon

1
2
navigator.sendBeacon(url);
navigator.sendBeacon(url, data);
  • url: 指定将要被发送到的网络地址;
  • data: 可选,是将要发送的 ArrayBufferArrayBufferViewBlobDOMStringFormDataURLSearchParams 类型的数据。
  • return: 返回值。当用户代理成功把数据加入传输队列时,**sendBeacon()** 方法将会返回 true,否则返回 false
1
2
3
4
5
6
7
8
// 通过 Blob 方式传递 JSON 数据
const blob = new Blob(
[JSON.stringify({ ... })],
{ type: 'application/json; charset=UTF-8' }
);
// 发送请求
navigator.sendBeacon(url, blob); // POST

sendBeacon 发送请求有以下几个特点:

  1. 通过 HTTP POST 请求方式 异步 发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能;
  2. 支持跨域,但不支持自定义 headers 请求头,这意味着:如果用户信息 Access-Token 是作为请求头信息传递,需要后台接口支持 url querystring 参数传递解析。
  3. 考虑其兼容性。

fetch

1
2
3
4
5
6
7
8
9
fetch(url, {
method: 'POST',
body: JSON.stringify({ ... }),
headers: {
'Content-Type': 'application/json', // 指定 type
},
keepalive: true, // 设置为 true,即便页面被终止请求也会保持连接
});

但它也有一些限制需要注意:

  1. 传输数据大小限制:无法发送兆字节的数数据,我们可以并行执行多个 keepalive 请求,但它们的 body 长度之和不得超过 64KB
  2. 无法处理服务器响应:在网页文档卸载后,尽管设置 keepalive 的 fetch 请求可以成功,但后续的响应处理无法工作。所以在大多数情况下,例如发送统计信息,这不是问题,因为服务器只接收数据,并通常向此类请求发送空的响应。

其他

对于无法查看控制台的页面

可以考虑把错误上发到后台查看日志

例如:

1
2
3
4
5
6
7
8
9
10
11
12
// web
Tools.sendErrorLog = function(...args) {
console.error('[weberror]', ...args);
Tools.socket && Tools.socket.emit('weberror', {
msg: JSON.stringify(args),
boardName: Tools.boardName
});
}
// 后端
socket.on("weberror", noFail(function onError(error) {
log("[WEB_ERROR]", error);
}));
本文作者:xb18
本文链接:http://xb18.github.io/2023/10/26/%E5%89%8D%E7%AB%AF%E5%9F%8B%E7%82%B9/
版权声明:本文采用 CC BY-NC-SA 3.0 CN 协议进行许可