前言

最近看过 Google Analytics 的客户端 ga-lite 源码,它使用了 SendBeacon API 来发送相关信息到 Google 服务器。简单查了查后发现,SendBeacon API 现在已经广泛用于各种统计/追踪代码的实现中。于是我对 SendBeacon API 做了下了解。

背景介绍

navigator.sendBeacon() 方法可用于通过HTTP将少量数据异步传输到Web服务器。

也许有人会疑惑为何不直接使用 XHR 直接使用 POST/GET 发送数据到 Web 服务器。SendBeacon API 诞生的意义在于满足追踪统计代码的需要,一般的统计代码都需要记录页面的停留时间,在没有这个 API 之前,有以下几种做法:

  1. unload 事件中,使用 XHR 异步发送数据。这种做法有可能导致服务端未收到数据,浏览器就已经断开连接;

  2. unload 事件中,使用 XHR 同步发送数据。这种做法会阻塞页面的跳转,影响用户体验。

  3. unload 事件中,创建一个图片对象并设置 src 属性。因为大多数用户代理会延迟卸载以保证图片加载完毕,从而达到在文档卸载前发送数据。

这几种做法在以前都属于比较 hack 的方法去实现在文档卸载期间发送统计数据到服务端。

于是 Beacon API 就诞生了。它的特点有:

  • 只发送数据,不需要解析 Response,单向的(one-way);
  • 优先级较低;
  • 保证在页面卸载前完成 Beacon 请求,不需要阻塞用户操作;

用法

使用 SendBeacon API 非常简单,几行代码足以完成所有工作:

1
2
3
const form = new Form()
form.append('trackId', 'tid')
navigator.sendBeacon('/path/to/log', form)

兼容性

Beacon API 的浏览器兼容性如下图所示:

Beacon API compability
Beacon API compability

在不支持的浏览器中,可以使用以下 fallback 代码解决浏览器不支持 Beancon API 的问题(仅实现了 GET 方法)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
function sendBeacon (url) {
  if (typeof navigator !== 'undefined' && navigator.sendBeacon) {
    return navigator.sendBeacon(url);
  }

  try {
    var req = new window.XMLHttpRequest();
    req.open('GET', url, false);
    req.send();
  } catch (e) {
    // Fix IE9 cross-site error
    var img = new window.Image();
    img.src = url;
  }
}

Reference