一、前言
上一篇文章我们介绍了关于前端监控的相关内容,包括前端监控的分类,埋点上报的方式,以及采用图片(GIF)做埋点有哪些好处,想了解的小伙伴可以先看这里! 那么这篇文章的话,我们来认识下前端监控中埋点数据上报的另外一种方式 Navigator.sendBeacon
!
二、基本介绍
Navigator.sendBeacon 是目前通用的埋点上报方案,可用于通过 HTTP POST 将少量数据 异步 传输到 Web 服务器。它主要用于将统计数据发送到 Web 服务器,同时避免了用传统技术(如:XMLHttpRequest
)发送分析数据的一些问题。
基本语法如下:Navigator.sendBeacon方法接受两个参数,第一个参数是目标服务器的 URL,第二个参数是所要发送的数据(可选),可以是任意类型(字符串、表单对象、二进制对象等)
js
navigator.sendBeacon(url);
navigator.sendBeacon(url, data);
当用户代理成功把数据加入传输队列时,sendBeacon()
方法将会返回 true
,否则返回 false
相较于img标签,使用 navigator.sendBeacon 会更规范,数据传输上可传输资源类型会更多。sendBeacon 是异步的,不会影响当前页到下一个页面的跳转速度,且不受同域限制。这个方法还是异步发出请求,但是请求与当前页面脱离关联,作为浏览器的任务,因此可以保证会把数据发出去,不拖延卸载流程。
三、痛点分析
这个方法主要用于满足统计和诊断代码的需要,这些代码通常尝试在卸载(unload)文档之前向 web 服务器发送数据。过早的发送数据可能导致错过收集数据的机会。然而,对于开发者来说保证在文档卸载期间发送数据一直是一个困难,因为用户代理通常会忽略在 unload
事件处理器中产生的异步 XMLHttpRequest
。
为了解决这个问题, 统计和诊断代码通常要在 unload
或者 beforeunload
事件处理器中发起一个同步 XMLHttpRequest
来发送数据。同步的 XMLHttpRequest
迫使用户代理延迟卸载文档,并使得下一个导航出现的更晚,下一个页面对于这种较差的载入表现无能为力。
过去,为了解决这个问题,统计和诊断代码通常要在
- 发起一个同步
XMLHttpRequest
来发送数据 - 创建一个
<img>
元素并设置src
,大部分用户代理会延迟卸载(unload)文档以加载图像 - 创建一个几秒的 no-op 循环
下面的例子展示了一个理论上的统计代码------在卸载事件处理器中尝试通过一个同步的 XMLHttpRequest
向服务器发送数据,导致了页面卸载被延迟。
js
window.addEventListener('unload', logData, false);
function logData() {
var client = new XMLHttpRequest();
client.open("POST", "/log", false); // 第三个参数表明是同步的 xhr
client.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
client.send(analyticsData);
}
这就是 sendBeacon()
方法存在的意义。使用 sendBeacon()
方法会使用户代理在有机会时异步地向服务器发送数据,同时不会延迟页面的卸载或影响下一导航的载入性能,这意味着:
- 数据发送是可靠的
- 数据异步传输
- 不影响下一导航的载入
四、发送数据的时机
网站通常希望在用户完成页面浏览后向服务器发送分析或诊断数据,最可靠的方法是在 visibilitychange
事件发生时发送数据:
javascript
document.addEventListener("visibilitychange", function logData() {
if (document.visibilityState === "hidden") {
navigator.sendBeacon("/log", analyticsData);
}
});
我们应该避免使用 unload 和 beforeunload 事件,过去,许多网站使用 unload
事件以在会话结束时发送统计数据。然而这是不可靠的,在许多情况下(尤其是移动设备)浏览器不会产生 unload
、beforeunload
或 pagehide
事件。下面列出了一种不触发上述事件的情况:
- 用户加载了网页并与其交互
- 完成浏览后,用户切换到了其他应用程序,而不是关闭选项卡
- 随后,用户通过手机的应用管理器关闭了浏览器应用
此外,unload
事件与现代浏览器实现的往返缓存不兼容
五、常见的埋点行为
1. 点击触发埋点
scss
function clickButton(url, data) {
navigator.sendBeacon(url, data)
}
2. 页面停留上报
路由文件中,初始化一个startTime,当页面离开时通过路由守卫计算停留时间
ini
let url = ''// 上报地址
let startTime = Date.now()
let currentTime = ''
router.beforeEach((to, from, next) => {
if (to) {
currentTime = Date.now()
stayTime = parseInt(currentTime - startTime)
navigator.sendBeacon(url, {time: stayTime})
startTime = Date.now()
}
})
3. 错误监听埋点
(1)Vue 错误捕获
lua
app.config.errorHandler = (err) => {
navigator.sendBeacon(url, {error: error.message, text: 'vue运行异常' })
}
(2)JS异常与静态资源加载异常,error 可以监听所有同步、异步的运行时错误,但无法监听语法、接口、资源加载错误
vbnet
window.addEventListener('error', (error) => {
if (error.message) {
navigator.sendBeacon(url, {error: error.message, text: 'js执行异常' })
} else {
navigator.sendBeacon(url, {error: error.filename, text: '资源加载异常' })
}
}, true)
(3)unhandledrejection 可以监听到 Promise 中抛出的,未被 .catch 捕获的错误
javascript
window.addEventListener('unhandledrejection')
(4)请求错误捕获
javascript
axios.interceptors.response.use(
(response) => {
if (response.code == 200) {
return Promise.resolve(response);
} else {
return Promise.reject(response);
}
},
(error) => {
// 返回错误逻辑
navigator.sendBeacon(url, {error: error, text: '请求错误异常' })
}
);
3. 内容可见埋点
javascript
// 可见性发生变化后的回调
function callback(data) {
navigator.sendBeacon(url, { target: data[0].target, text: '内容可见' })
}
// 交叉观察器配置项
let options = {};
// 生成交叉观察器
const observer = new IntersectionObserver(callback);
// 获取目标节点
let target = document.getElementById("target");
// 监听目标元素
observer.observe(target);