Navigator.sendBeacon() 方法详解与数据埋点

Navigator.sendBeacon() 方法详解

navigator.sendBeacon() 是现代浏览器提供的一个API,专门用于在页面卸载(unload)阶段向服务器可靠地发送少量数据。

基本概念

用途

sendBeacon 主要用于解决传统方法(如XMLHttpRequest或fetch)在页面卸载时无法可靠发送数据的问题。常见使用场景包括:

  • 发送分析数据(如页面访问统计)
  • 记录用户行为(如最后停留时间)
  • 上报错误日志
  • 保存用户表单输入(防止意外关闭时丢失)

语法

navigator.sendBeacon(url, data);
  • url: 接收数据的服务器URL
  • data: 要发送的数据,可以是ArrayBuffer、ArrayBufferView、Blob、DOMString、FormData或URLSearchParams

返回值

返回布尔值:

  • true: 浏览器已成功将数据加入发送队列
  • false: 发送失败(如URL无效、数据过大等)

主要特点

  1. 异步且不阻塞页面卸载

    • 不会延迟页面卸载或导航
    • 浏览器会在后台处理请求
  2. 高优先级

    • 相比普通AJAX请求有更高的发送优先级
  3. 跨域支持

    • 遵循CORS规则
    • 可以携带cookie(需设置credentials)
  4. 可靠传输

    • 即使在页面关闭后,浏览器也会尽量确保请求完成
  5. 数据大小限制

    • 不同浏览器实现不同,通常限制在64KB以内

与传统方法的对比

特性sendBeaconXMLHttpRequestfetchImage Beacon
卸载时可用✔️✔️
不阻塞卸载✔️✔️
支持POST✔️✔️✔️❌ (GET only)
支持多种数据类型✔️✔️✔️
异步处理✔️✔️✔️✔️
CORS支持✔️✔️✔️✔️

使用示例

基本用法

// 在页面卸载时发送数据
window.addEventListener('unload', function() {
  const data = JSON.stringify({
    page: window.location.href,
    time: Date.now()
  });
  navigator.sendBeacon('/analytics', data);
});

发送FormData

const data = new FormData();
data.append('event', 'pageview');
data.append('timestamp', Date.now());
navigator.sendBeacon('/track', data);

带有类型信息的Blob数据

const blob = new Blob([JSON.stringify({ key: 'value' })], {
  type: 'application/json'
});
navigator.sendBeacon('/log', blob);

最佳实践

  1. 数据量控制

    • 保持数据尽可能小,以提高可靠性
  2. 错误处理

    if (!navigator.sendBeacon(url, data)) {
      // 备用方案
      fetch(url, {
        method: 'POST',
        body: data,
        keepalive: true
      });
    }
    
  3. Content-Type设置

    • 对于非FormData/URLSearchParams数据,服务器可能需要特殊处理
    • 可以通过Blob指定类型
  4. 与visibilitychange事件结合

    document.addEventListener('visibilitychange', function() {
      if (document.visibilityState === 'hidden') {
        navigator.sendBeacon('/visibility-change', data);
      }
    });
    

浏览器兼容性

  • 现代浏览器普遍支持(Chrome 39+, Firefox 31+, Edge 14+, Safari 11.1+)
  • 不支持IE
  • 移动端浏览器支持良好

注意事项

  1. 无法自定义请求头

    • 只能通过数据本身或URL传递信息
  2. 无法获取响应

    • 单向发送,不关心服务器响应
  3. 可能被ad-blockers拦截

    • 某些广告拦截器会拦截beacon请求
  4. 性能影响

    • 虽然设计为低影响,但大量使用仍可能影响性能

替代方案

如果sendBeacon不可用,可以考虑:

  1. fetch with keepalive 标志
    fetch(url, {
      method: 'POST',
      body: data,
      keepalive: true
    });
    
  2. 传统的Image Beacon(仅限GET请求)
    new Image().src = '/track.gif?' + queryString;
    

服务器端处理

服务器接收到的请求与普通HTTP请求类似,但要注意:

  • 可能需要处理不同的Content-Type
  • 请求可能在页面关闭后到达
  • 可能没有Referer头信息

navigator.sendBeacon()是为解决特定场景下的数据上报问题而设计的专用API,在分析统计、日志记录等场景下能提供更可靠的解决方案。

使用Navigator.sendBeacon()进行数据埋点的完整指南

数据埋点是网站分析的基础,navigator.sendBeacon()因其在页面卸载时的可靠性成为埋点的理想选择。下面详细介绍如何使用这一技术进行全方位数据埋点。

一、基础埋点实现

1. 页面访问跟踪

// 页面展现埋点
document.addEventListener('DOMContentLoaded', function() {
  const pageViewData = {
    event: 'pageview',
    url: window.location.href,
    referrer: document.referrer,
    timestamp: Date.now(),
    title: document.title,
    screen: `${window.screen.width}x${window.screen.height}`,
    language: navigator.language
  };
  
  navigator.sendBeacon('/track', JSON.stringify(pageViewData));
});

2. 用户行为跟踪

// 按钮点击跟踪
document.querySelectorAll('[data-track]').forEach(element => {
  element.addEventListener('click', function() {
    const clickData = {
      event: 'click',
      element: this.getAttribute('data-track'),
      text: this.textContent.trim(),
      href: this.href || '',
      position: getElementPosition(this), // 自定义获取元素位置函数
      timestamp: Date.now()
    };
    
    navigator.sendBeacon('/track', JSON.stringify(clickData));
  });
});

// 滚动深度跟踪
let lastReportedScroll = 0;
window.addEventListener('scroll', _.throttle(() => {
  const scrollDepth = getScrollDepth(); // 获取滚动百分比
  if (scrollDepth - lastReportedScroll > 10) { // 每增加10%报告一次
    const scrollData = {
      event: 'scroll',
      depth: scrollDepth,
      timestamp: Date.now()
    };
    navigator.sendBeacon('/track', JSON.stringify(scrollData));
    lastReportedScroll = scrollDepth;
  }
}, 1000));

二、高级埋点策略

1. 会话跟踪

// 生成唯一会话ID
const sessionId = 'session_' + Math.random().toString(36).substr(2, 9);
sessionStorage.setItem('sessionId', sessionId);

// 页面卸载时发送会话结束信号
window.addEventListener('beforeunload', function() {
  const sessionData = {
    event: 'session_end',
    sessionId: sessionId,
    duration: getSessionDuration(), // 计算会话时长
    pageCount: getPageViewCount(),  // 获取本次会话页面浏览数
    timestamp: Date.now()
  };
  
  navigator.sendBeacon('/track', JSON.stringify(sessionData));
});

2. 性能监控

// 使用Performance API获取性能数据
window.addEventListener('load', function() {
  const timing = performance.timing;
  const perfData = {
    event: 'performance',
    dns: timing.domainLookupEnd - timing.domainLookupStart,
    tcp: timing.connectEnd - timing.connectStart,
    ttfb: timing.responseStart - timing.requestStart,
    domReady: timing.domComplete - timing.domLoading,
    loadTime: timing.loadEventEnd - timing.navigationStart,
    memory: performance.memory ? performance.memory.usedJSHeapSize : null
  };
  
  navigator.sendBeacon('/track', JSON.stringify(perfData));
});

3. 错误追踪

// 全局错误捕获
window.onerror = function(message, source, lineno, colno, error) {
  const errorData = {
    event: 'error',
    message: message,
    source: source,
    line: lineno,
    column: colno,
    stack: error && error.stack,
    timestamp: Date.now()
  };
  
  navigator.sendBeacon('/track', JSON.stringify(errorData));
};

// Promise未捕获异常
window.addEventListener('unhandledrejection', function(event) {
  const promiseData = {
    event: 'unhandled_rejection',
    reason: event.reason,
    timestamp: Date.now()
  };
  
  navigator.sendBeacon('/track', JSON.stringify(promiseData));
});

三、优化技巧

1. 数据压缩

function compressData(data) {
  // 简单示例:移除空格缩短JSON
  return JSON.stringify(data).replace(/\s+/g, '');
  
  // 生产环境可考虑更复杂的压缩算法
}

2. 批量发送

const batchQueue = [];
const BATCH_SIZE = 5;
const BATCH_TIMEOUT = 5000;

function trackEvent(eventData) {
  batchQueue.push(eventData);
  
  if (batchQueue.length >= BATCH_SIZE) {
    sendBatch();
  } else if (!batchTimer) {
    batchTimer = setTimeout(sendBatch, BATCH_TIMEOUT);
  }
}

function sendBatch() {
  if (batchQueue.length > 0) {
    navigator.sendBeacon('/track', JSON.stringify({
      event: 'batch',
      items: batchQueue
    }));
    batchQueue.length = 0;
  }
  clearTimeout(batchTimer);
  batchTimer = null;
}

// 页面卸载时确保发送剩余数据
window.addEventListener('beforeunload', sendBatch);

3. 用户识别

// 生成持久用户ID
function getUserId() {
  let userId = localStorage.getItem('userId');
  if (!userId) {
    userId = 'user_' + Math.random().toString(36).substr(2, 9);
    localStorage.setItem('userId', userId);
  }
  return userId;
}

// 在埋点数据中包含用户ID
const baseData = {
  userId: getUserId(),
  sessionId: sessionStorage.getItem('sessionId'),
  // 其他通用字段...
};

四、服务器端处理建议

1. Node.js示例

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

app.post('/track', bodyParser.text(), (req, res) => {
  try {
    const data = JSON.parse(req.body);
    
    // 验证必要字段
    if (!data.event || !data.timestamp) {
      return res.status(400).send('Invalid data');
    }
    
    // 添加服务器时间
    data.serverTime = new Date().toISOString();
    
    // 存储到数据库或分析系统
    saveToDatabase(data);
    
    res.status(200).send('OK');
  } catch (err) {
    console.error('Tracking error:', err);
    res.status(500).send('Error');
  }
});

function saveToDatabase(data) {
  // 实现数据库存储逻辑
  console.log('Tracking data:', data);
}

2. 数据存储建议

  • 数据结构化: 使用专门的分析数据库(如ClickHouse)
  • 数据分区: 按日期/事件类型分区提高查询效率
  • 数据清洗: 存储前验证和清理数据

五、调试与验证

1. 开发环境调试

// 重写sendBeacon进行调试
if (process.env.NODE_ENV === 'development') {
  navigator.sendBeacon = function(url, data) {
    console.log('Beacon Data:', JSON.parse(data));
    return true;
  };
}

2. 网络请求监控

使用Chrome开发者工具的Network面板:

  • 过滤"beacon"类型请求
  • 检查请求负载和响应
  • 验证CORS头部是否正确设置

3. 数据质量检查

  • 完整性检查: 确保所有关键事件都被捕获
  • 一致性检查: 验证跨页面用户/会话ID是否一致
  • 时间序列分析: 检查时间戳是否合理

六、隐私与合规考虑

  1. GDPR/CCPA合规

    // 检查用户同意状态
    function canTrack() {
      return window.consent && window.consent.analytics;
    }
    
    // 在所有埋点调用前检查
    if (canTrack()) {
      navigator.sendBeacon('/track', data);
    }
    
  2. 数据匿名化

    // 移除或哈希处理PII(个人身份信息)
    function anonymize(data) {
      if (data.email) {
        data.emailHash = sha256(data.email);
        delete data.email;
      }
      return data;
    }
    
  3. DNT(Do Not Track)尊重

    if (navigator.doNotTrack !== '1') {
      // 执行跟踪代码
    }
    

通过以上方法,您可以构建一个全面、可靠且高效的数据埋点系统。navigator.sendBeacon()的特别优势在于能够可靠捕获用户离开页面前的最后行为,这是传统AJAX请求难以实现的。结合其他跟踪技术,可以获取完整的用户行为画像。

<think>好的,用户想了解如何设置navigator.sendBeacon发送失败的数据格式为JSON。首先,我需要回忆一下sendBeacon的基本用法和相关限制。 首先,sendBeacon方法用于异步发送少量数据到服务器,通常用于在页面卸载时发送日志或统计数据。它的特点是可靠且不阻塞页面卸载。语法是navigator.sendBeacon(url, data),其中data可以是Blob、ArrayBuffer、FormData等类型。 用户提到发送失败的数据格式为JSON,但sendBeacon本身并不直接支持JSON字符串作为参数。所以需要将数据转换为支持的格式。比如,可以使用Blob类型,将JSON字符串包装进去,并设置正确的MIME类型,如'application/json'。 接下来需要考虑如何处理发送失败的情况。sendBeacon返回一个布尔值,表示是否成功排队发送。但需要注意的是,它不提供异步的错误处理,比如Promise或回调。因此,如果发送失败,可能需要使用其他方法如fetch或XMLHttpRequest来重试,但这些方法在页面卸载时可能不可靠。 然后,我需要验证如何正确构造Blob对象。例如,将数据对象转换为JSON字符串,然后创建Blob实例。同时,设置请求头可能需要通过Blob的type属性,或者在服务器端正确解析。 另外,可能需要提醒用户注意sendBeacon的大小限制,不同浏览器可能有不同的限制,通常建议数据量较小。如果数据太大,sendBeacon可能会失败,这时候可能需要分批次发送或改用其他方法。 还需要考虑兼容性问题,确保目标浏览器支持sendBeacon和Blob类型。对于旧版本浏览器,可能需要提供回退方案。 最后,总结步骤:构造JSON数据,转换为Blob,设置正确MIME类型,发送并处理可能的失败情况。同时,提供错误处理示例,比如使用try-catch,或者使用其他方法进行重试。</think>以下是设置navigator.sendBeacon发送JSON数据格式的逐步说明: 1. **数据转换关键步骤**: ```javascript const data = { event: 'page_unload', timestamp: Date.now() }; // 将JSON对象转换为Blob格式 const blob = new Blob([JSON.stringify(data)], { type: 'application/json; charset=UTF-8' }); ``` 2. **发送请求标准方法**: ```javascript const url = '/analytics-endpoint'; if (!navigator.sendBeacon(url, blob)) { // 发送失败处理逻辑 console.error('Beacon发送失败,启动备用方案'); // 备用方案示例:使用fetch+keepalive fetch(url, { method: 'POST', body: JSON.stringify(data), headers: { 'Content-Type': 'application/json' }, keepalive: true }).catch(e => console.warn('备用方案也失败', e)); } ``` **注意事项**: 1. **容量限制**:不同浏览器对Blob大小限制不同(Chrome约64KB,Firefox约256KB),建议控制数据体积 2. **请求头设置**:通过Blob type参数设置的Content-Type可能需要服务端支持 3. **错误处理**:建议配合使用`window.addEventListener('unload', ...)`确保触发时机 4. **性能优化**:提前创建Blob对象避免在unload事件中执行复杂操作 **调试建议**: ```javascript // 添加事件监听验证请求 navigator.sendBeacon(url, blob); window.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') { // 检查网络面板中的请求头 } }); ``` **服务端配置示例**(Node.js/Express): ```javascript app.post('/analytics-endpoint', express.json(), (req, res) => { console.log('Received:', req.body); res.sendStatus(200); }); ``` 通过这种方式,既能保证数据格式正确,又能处理发送失败的情况,同时符合现代浏览器的性能优化要求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值