Navigator.sendBeacon API 教程

Navigator.sendBeacon API 教程

简介

navigator.sendBeacon() 是一个 Web API,它提供了一种可靠的方式来发送小量数据到服务器,特别适用于在页面卸载(unload)时发送分析或诊断数据。与传统的 XMLHttpRequest 或 fetch 不同,sendBeacon 请求不会因为页面导航或关闭而被取消。

为什么需要 sendBeacon?

在 sendBeacon 出现之前,开发者通常使用以下方法在页面卸载时发送数据:

  1. unloadbeforeunload 事件中使用同步 XMLHttpRequest
  2. 设置 navigator.sendBeacon = false 以延迟页面卸载
  3. 使用 <img> 标签的 src 属性发送信标请求

这些方法都有不同的缺点:同步请求会阻塞页面卸载,影响用户体验;延迟卸载也会导致页面响应变慢;而图片信标方法可能不可靠。

sendBeacon API 解决了这些问题,它保证了:

  • 请求以异步方式处理,不会阻塞页面导航
  • 浏览器会尽最大努力发送这些请求,即使页面已关闭
  • 简单易用,无需复杂的配置

基本语法

const success = navigator.sendBeacon(url, data);

参数:

  • url:接收数据的服务器端点

  • data
    

    (可选):要发送的数据,可以是以下类型:

    • ArrayBuffer
    • ArrayBufferView
    • Blob
    • DOMString
    • FormData
    • URLSearchParams

返回值:

  • 布尔值,表示浏览器是否已将数据加入到发送队列中

使用示例

基本用法

window.addEventListener('unload', () => {
  navigator.sendBeacon('/analytics', 'page-visit-ended');
});

发送 JSON 数据

window.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    const analyticsData = {
      event: 'page_hide',
      timeSpent: calculateTimeSpent(),
      scrollDepth: getScrollDepth()
    };
    
    const blob = new Blob([JSON.stringify(analyticsData)], {type: 'application/json'});
    navigator.sendBeacon('/analytics/events', blob);
  }
});

使用 FormData

window.addEventListener('beforeunload', () => {
  const formData = new FormData();
  formData.append('event', 'page_unload');
  formData.append('userId', getUserId());
  formData.append('sessionDuration', getSessionDuration());
  
  navigator.sendBeacon('/analytics/collect', formData);
});

最佳实践

  1. 数据量控制:sendBeacon 适合发送小量数据,大多数浏览器限制为 64KB,超过这个大小可能会失败。

  2. 选择正确的事件

    • visibilitychange:页面变为不可见时触发,比 unload 更可靠
    • pagehide:页面隐藏或卸载时触发
    • beforeunload:页面即将卸载时触发
    • unload:页面卸载时触发(不推荐,但仍然广泛使用)
  3. 检查浏览器支持

    if (navigator.sendBeacon) {
      // 使用 sendBeacon
    } else {
      // 回退到其他方法
    }
    
  4. 检查发送状态

    const success = navigator.sendBeacon('/endpoint', data);
    if (!success) {
      // 发送失败,考虑回退方法
    }
    
  5. 服务器端处理:服务器应配置为正确处理 POST 请求,Content-Type 通常为:

    • text/plain(字符串数据)
    • application/json(JSON 数据)
    • multipart/form-data(FormData 对象)

浏览器兼容性

sendBeacon API 在现代浏览器中得到广泛支持:

  • Chrome 39+
  • Firefox 31+
  • Safari 11.1+
  • Edge 14+
  • Opera 26+

移动浏览器:

  • Android Chrome 39+
  • iOS Safari 11.3+

实际应用场景

  1. 分析追踪:记录用户行为和页面性能数据
  2. 会话日志:记录用户会话的结束时间和持续时间
  3. 错误报告:在页面崩溃或关闭前发送错误日志
  4. 用户活动跟踪:记录页面交互数据
  5. 广告展示统计:发送广告展示和交互数据

与其他技术的比较

特性sendBeaconXMLHttpRequestFetch图片信标
页面卸载时可靠性低(除非同步)
阻塞页面导航可能(如果同步)
支持多种数据格式否(仅 URL 参数)
可控制请求头
可接收响应有限

限制和注意事项

  1. 没有响应处理:sendBeacon 是单向通信,无法处理服务器响应
  2. 无法设置请求头:自定义请求头不被支持
  3. 数据大小限制:通常限制为 64KB
  4. 没有重试机制:如果发送失败,不会自动重试
  5. CORS 限制:仍然受到同源策略的限制,跨域请求需要服务器支持

完整示例:用户行为跟踪

class AnalyticsTracker {
  constructor() {
    this.data = {
      startTime: Date.now(),
      pageViews: 1,
      clicks: 0,
      scrollDepth: 0,
      url: window.location.href
    };
    
    this.setupEventListeners();
  }
  
  setupEventListeners() {
    // 记录点击
    document.addEventListener('click', () => {
      this.data.clicks++;
    });
    
    // 记录滚动深度
    window.addEventListener('scroll', () => {
      const scrollTop = window.scrollY;
      const docHeight = document.body.offsetHeight;
      const winHeight = window.innerHeight;
      const scrollPercent = scrollTop / (docHeight - winHeight);
      this.data.scrollDepth = Math.max(
        this.data.scrollDepth, 
        Math.round(scrollPercent * 100)
      );
    });
    
    // 页面隐藏时发送数据
    document.addEventListener('visibilitychange', () => {
      if (document.visibilityState === 'hidden') {
        this.sendData();
      }
    });
    
    // 页面卸载时发送数据
    window.addEventListener('beforeunload', () => {
      this.sendData();
    });
  }
  
  sendData() {
    // 计算停留时间
    this.data.timeSpent = Date.now() - this.data.startTime;
    
    // 准备发送的数据
    const blob = new Blob(
      [JSON.stringify(this.data)], 
      {type: 'application/json'}
    );
    
    // 使用 sendBeacon 发送数据
    if (navigator.sendBeacon('/analytics/collect', blob)) {
      console.log('Analytics data queued for sending');
    } else {
      console.error('Failed to queue analytics data');
      // 可以实现备选方案
    }
  }
}

// 初始化跟踪器
const tracker = new AnalyticsTracker();

服务器端示例(Node.js)

const express = require('express');
const bodyParser = require('body-parser');
const app = express();

// 支持 JSON 请求体
app.use(bodyParser.json());
// 支持 URL 编码的请求体
app.use(bodyParser.urlencoded({ extended: true }));
// 支持原始请求体
app.use(bodyParser.raw({ type: '*/*' }));

// 处理 beacon 请求的端点
app.post('/analytics/collect', (req, res) => {
  let data;
  
  // 根据 Content-Type 解析数据
  const contentType = req.headers['content-type'] || '';
  
  if (contentType.includes('application/json')) {
    data = req.body; // 已由 bodyParser.json() 解析
  } else if (contentType.includes('text/plain')) {
    data = req.body.toString();
  } else if (contentType.includes('application/x-www-form-urlencoded')) {
    data = req.body; // 已由 bodyParser.urlencoded() 解析
  } else {
    // 处理其他格式
    data = req.body;
  }
  
  // 记录或处理数据
  console.log('Received beacon data:', data);
  
  // sendBeacon 不关心响应,但我们仍然发送一个
  res.status(202).send('Accepted');
});

app.listen(3000, () => {
  console.log('Server listening on port 3000');
});

总结

Navigator.sendBeacon API 为网站提供了一种可靠的方式来发送用户离开页面时的数据,而不会影响用户体验。它特别适合分析、日志记录和诊断数据的收集。虽然它有一些限制,如无法处理响应和自定义请求头,但对于它的设计目的来说,这是一个简单而有效的解决方案。

随着用户隐私意识的提高和浏览器策略的变化,开发者应该负责任地使用这个 API,确保透明地收集数据并遵守相关的隐私法规。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值