前端监控系列——Navigator.sendBeacon上报数据🚀🚀🚀

一、前言

上一篇文章我们介绍了关于前端监控的相关内容,包括前端监控的分类,埋点上报的方式,以及采用图片(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 事件以在会话结束时发送统计数据。然而这是不可靠的,在许多情况下(尤其是移动设备)浏览器不会产生 unloadbeforeunloadpagehide 事件。下面列出了一种不触发上述事件的情况:

  1. 用户加载了网页并与其交互
  2. 完成浏览后,用户切换到了其他应用程序,而不是关闭选项卡
  3. 随后,用户通过手机的应用管理器关闭了浏览器应用

此外,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);
相关推荐
xump6 小时前
如何在DevTools选中调试一个实时交互才能显示的元素样式
前端·javascript·css
折翅嘀皇虫6 小时前
fastdds.type_propagation 详解
java·服务器·前端
Front_Yue6 小时前
深入探究跨域请求及其解决方案
前端·javascript
wordbaby6 小时前
React Native 进阶实战:基于 Server-Driven UI 的动态表单架构设计
前端·react native·react.js
抱琴_6 小时前
【Vue3】我用 Vue 封装了个 ECharts Hooks,同事看了直接拿去复用
前端·vue.js
风止何安啊6 小时前
JS 里的 “变量租房记”:闭包是咋把变量 “扣” 下来的?
前端·javascript·node.js
Danny_FD7 小时前
用 ECharts markLine 标注节假日
前端·echarts
程序员西西7 小时前
SpringBoot无感刷新Token实战指南
java·开发语言·前端·后端·计算机·程序员
烛阴7 小时前
Luban集成CocosCreator完整教程
前端·typescript·cocos creator
有点笨的蛋7 小时前
深入理解 JavaScript 原型机制:构造函数、原型对象与原型链
前端·javascript