sendBeacon 实现数据上报

4,721 阅读2分钟

sendBeacon

监听页面在关闭或者刷新时 beforeunload 或者 页面正在被卸载前unload 触发;

Beacon API 用于将少量数据通过 post 请求发送到服务器;

Beacon 是非阻塞请求,不需要响应。

  • 场景

Beacon 接口用于将异步和非阻塞请求发送到服务器。Beacon 请求使用 HTTP 协议中的 POST 方法,请求通常不需要响应。这个请求可以保证在页面的 unload 状态从发起到完成之前被发送。而并不需要一个阻塞请求,例如 XMLHttpRequest 。该交互方式非常适合用在监控捕捉数据方面,不需要得到响应。最终要的是:不会延迟页面的卸载或影响下一导航的载入性能。

  • 用法
navigator.sendBeacon(url, data);

url:表示 data 将要被发送到的网络地址;

data:将要发送的 ArrayBufferViewBlob, DOMString 或者 FormData 类型的数据。

返回值:当用户代理成功把数据加入传输队列时,sendBeacon() 方法将会返回 true,否则返回 false

  • 支持的类型

text/plain:发送普通字符串

目前在 Beacon 中最常用的形式:

navigator.sendBeacon(url, JSON.stringify(data));

Bolb 类型: 发送 Bolb 对象

let blob = new Blob([JSON.stringify(data), {
    type: 'application/json',
    // type: 'application/x-www-form-urlencoded',
}]);
navigator.sendBeacon(url, blob);

application/x-www-form-urlencoded:发送 URLSearchParams 对象

navigator.sendBeacon(url, new URLSearchParams(data));

multipart/form-data:发送 FormData 对象

let formData = new FormData();
formData.append('text', '测试');
navigator.sendBeacon(url, formData);
  • 实例

记录在页面上停留的时间

let log = function() {
    // 测试是否支持 Beacon
    if (!navigator.sendBeacon) return true;
    const data = {
        start: new Date().getTime(),
        end: performance.now(),
        url: ''
    }
    navigator.sendBeacon(url, JSON.stringfy(data));
};
window.addEventListener('beforeunload', log);
  • 存在的问题
  1. POST 提交数据,默认 content-typetext-plain。在 node + eggjs 服务器中获取不到对应的数据 this.ctx.request.body (拼接在接口地址后面的数据仍然可以使用 this.ctx.request.query 获取),数据被存储在 this.ctx.req (返回该类型数据 IncomingMessage {}),通过 co-body 解析生成需要的字段:
const parse = require('co-body');
const body = await parse.json(this.ctx.req);
  1. unload 事件:在 MacSafari 上似乎用安全警告阻止了请求,保险起见可以在 beforeunload 事件中使用
  • 兼容性