埋点项目实录之二:数据上报

952 阅读5分钟

数据上报

当收集到数据时何时上报以及如何上报,情况如下:

  1. 获得数据时,可以立即上报,也可以把数据存储在本地,等数据到达一定的数量时上报,或者每隔一段时间上报一次
  2. 使用SDK或者调用ajax上报时,一般会有回调函数,上报成功时,使用时间戳timestamp等唯一标识从本地存储移除
  3. 一般Google,FaceBook等使用GIF上报,Google和FaceBook分别提供了gtag和fbq等API,其中FaceBook对于上报的方式判断更为详细也有可能使用POST上报
  4. 当页面跳转时,会导致请求失败,一般可以借助回调函数的机制或者navigator.sendBeacon。当然也可以利用数据存储在本地,在跳转页面之后再进行上报。或者阻止页面的跳转,等数据上报成功之后,在进行跳转(此方式不建议)

GIF上报

  1. 避免跨域
  2. 防止阻塞页面加载,影响用户体验:不会阻塞页面加载,影响用户的体验,只要new Image对象就好了,一般情况下也不需要append到DOM中,通过它的onerror和onload事件来检测发送状态
  3. 相比PNG/JPG,GIF的体积最小:最小的BMP文件需要74个字节,PNG需要67个字节,而合法的GIF,只需要43个字节。同样的响应,GIF可以比BMP节约41%的流量,比PNG节约35%的流量。
<script type="text/javascript">
 var thisPage = location.href;
 var referringPage = (document.referrer) ? document.referrer : "none";
 var beacon = new Image();
 beacon.src = "http://www.example.com/logger/beacon.gif?page=" + encodeURI(thisPage)
 + "&ref=" + encodeURI(referringPage);
</script>

Navigator.sendBeacon()

用户卸载网页的时候,有时需要向服务器发一些数据。很自然的做法是在unload事件或beforeunload事件的监听函数里面,使用XMLHttpRequest对象发送数据。但是,这样做不是很可靠,因为XMLHttpRequest对象是异步发送,很可能在它即将发送的时候,页面已经卸载了,从而导致发送取消或者发送失败。

解决方法就是unload事件里面,加一些很耗时的同步操作。这样就能留出足够的时间,保证异步 AJAX 能够发送成功。

function log() {
  let xhr = new XMLHttpRequest();
  xhr.open('post', '/log', true);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.send('foo=bar');
}
window.addEventListener('unload', function(event) {
  log();
  // a time-consuming operation
  for (let i = 1; i < 10000; i++) {
    for (let m = 1; m < 10000; m++) { continue; }
  }
});

在用户点击时,延迟跳转。

const clickTime = 350;
const theLink = document.getElementById('target');
function log() {
  let xhr = new XMLHttpRequest();
  xhr.open('post', '/log', true);
  xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  xhr.send('foo=bar');
}
theLink.addEventListener('click', function (event) {
  event.preventDefault();
  log();
  setTimeout(function () {
    window.location.href = theLink.getAttribute('href');
  }, clickTime);
})

navigator.sendBeacon(url, data)

  • url: 表明 data 将要被发送到的网络地址
  • data: 将要发送的 ArrayBufferArrayBufferViewBlobDOMStringFormData 或 URLSearchParams 类型的数据 返回值一个布尔值,成功发送数据为true, 否则为false。 发送该数据的方法是POST,可以跨域,类似于表单提交数据,不能指定回调函数。

避免使用unload和beforeunload

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

  1. 用户点击了一条系统通知,切换到另一个 App。
  2. 用户进入任务切换窗口,切换到另一个 App。
  3. 用户点击了 Home 按钮,切换回主屏幕。
  4. 操作系统自动切换到另一个 App(比如,收到一个电话)

此外,unload 事件与现代浏览器实现的往返缓存(bfcache)不兼容。在部分浏览器(如:Firefox)通过在 bfcache 中排除包含 unload 事件处理器的页面来解决不兼容问题,但这存在性能损失。其它浏览器,例如 Safari 和 Android 上的 Chrome 浏览器则采取用户在同一标签页下导航至其它页面时不触发 unload 事件的方法来解决不兼容问题。

Firefox 也会在 bfcache 中排除包含 beforeunload 事件处理器的页面。

visibilitychange

通过监听网页的可见性,可以预判网页的卸载,还可以用来节省资源,减缓电能的消耗。比如,一旦用户不看网页,下面这些网页行为都是可以暂停的。

  • 对服务器的轮询
  • 网页动画
  • 正在播放的音频或视频

document.visibilityState

document对象上,新增了一个document.visibilityState属性。该属性返回一个字符串,表示页面当前的可见性状态,共有三个可能的值。

  • hidden:页面彻底不可见。
  • visible:页面至少一部分可见。
  • prerender:页面即将或正在渲染,处于不可见状态。

hidden状态和visible状态是所有浏览器都必须支持的。prerender状态只在支持"预渲染"的浏览器上才会出现,比如 Chrome 浏览器就有预渲染功能,可以在用户不可见的状态下,预先把页面渲染出来,等到用户要浏览的时候,直接展示渲染好的网页。

document.visibilityState属性为hidden,以下四种情况

  • 浏览器最小化。
  • 浏览器没有最小化,但是当前页面切换成了背景页。
  • 浏览器将要卸载(unload)页面。
  • 操作系统触发锁屏屏幕。

document.hidden

该属性只读,返回一个布尔值,表示当前页面是否可见。

document.visibilityState属性返回visible时,document.hidden属性返回false;其他情况下,都返回true

// 代码示例
document.addEventListener('visibilitychange', function () {
  // 用户离开了当前页面
  if (document.visibilityState === 'hidden') {
    document.title = '页面不可见';
  }

  // 用户打开或回到页面
  if (document.visibilityState === 'visible') {
    document.title = '页面可见';
  }
});

以上全部内容,如有疑问,欢迎指正。

8.webp