likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 12

一、前言

二、基本介绍

Navigator.sendBeacon 是目前通用的埋点上报方案,可用于通过 HTTP POST 将少量数据 异步 传输到 Web 服务器。它主要用于将统计数据发送到 Web 服务器,同时避免了用传统技术(如:XMLHttpRequest)发送分析数据的一些问题。

基本语法如下:Navigator.sendBeacon方法接受两个参数,第一个参数是目标服务器的 URL,第二个参数是所要发送的数据(可选),可以是任意类型(字符串、表单对象、二进制对象等)

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 向服务器发送数据,导致了页面卸载被延迟。

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 事件发生时发送数据:

document.addEventListener("visibilitychange", function logData() {
  if (document.visibilityState === "hidden") {
    navigator.sendBeacon("/log", analyticsData);
  }
});

我们应该避免使用 unload 和 beforeunload 事件,过去,许多网站使用 unload 事件以在会话结束时发送统计数据。然而这是不可靠的,在许多情况下(尤其是移动设备)浏览器不会产生 unloadbeforeunload 或 pagehide 事件。下面列出了一种不触发上述事件的情况:

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

此外,unload 事件与现代浏览器实现的往返缓存不兼容

五、常见的埋点行为

1. 点击触发埋点

function clickButton(url, data) {
    navigator.sendBeacon(url, data)
}

2. 页面停留上报

路由文件中,初始化一个startTime,当页面离开时通过路由守卫计算停留时间

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 错误捕获

app.config.errorHandler = (err) => { 
    navigator.sendBeacon(url, {error: error.message, text: 'vue运行异常' })
}

(2)JS异常与静态资源加载异常,error 可以监听所有同步、异步的运行时错误,但无法监听语法、接口、资源加载错误

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 捕获的错误

window.addEventListener('unhandledrejection')

(4)请求错误捕获

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. 内容可见埋点

// 可见性发生变化后的回调 
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);