提示:退出浏览器之前,发送积压的埋点数据请求,该如何做?
文章目录
1. 使用 navigator.sendBeacon()(推荐)
Beacon API 是浏览器专为统计/诊断日志设计的异步接口,能在页面卸载时可靠地发送数据,且不会阻塞页面关闭。
- 优点:
✅ 异步发送,不延迟页面关闭
✅ 即使页面关闭,请求仍会被浏览器保证执行
✅ 支持跨域请求
代码示例
// 在页面卸载时发送积压的埋点数据
window.addEventListener('beforeunload', () => {
const pendingData = ['event1', 'event2']; // 积压的埋点数据
const url = 'https://api.example.com/track';
pendingData.forEach(data => {
const blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
navigator.sendBeacon(url, blob);
});
});
提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。
注意事项
- 数据大小限制:部分浏览器限制 Beacon 请求体大小(Chrome 默认限制为 64KB)。
- 兼容性:IE 不支持,其他主流浏览器均支持。
2. 使用同步 XMLHttpRequest(兼容旧浏览器)
如果必须支持旧浏览器(如 IE),可使用同步请求,但会阻塞页面关闭流程,可能影响用户体验。 慎用:浏览器已逐步废弃同步请求,部分场景(如React/Vue 的路由跳转)可能无法生效。
代码示例
window.addEventListener('beforeunload', () => {
const pendingData = ['event1', 'event2'];
pendingData.forEach(data => {
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://api.example.com/track', false); // 同步请求
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));
});
});
- 缺点
❌ 阻塞页面关闭,可能导致浏览器警告弹窗(如 “此页面正在阻止你离开”)。
❌ 部分浏览器(如 Chrome 80+)已限制同步请求在 beforeunload 中的使用。
3. 结合 visibilitychange 事件(应对浏览器崩溃/直接关闭)
如果用户直接关闭浏览器(而非标签页),beforeunload 可能不触发。此时可监听页面隐藏事件(visibilitychange)作为补充。
代码示例
// 页面隐藏时(切换标签页、最小化窗口、关闭浏览器前)
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
navigator.sendBeacon('https://api.example.com/track', JSON.stringify({ event: 'page_hide' }));
}
});
// 页面关闭时(常规情况)
window.addEventListener('beforeunload', () => {
navigator.sendBeacon('https://api.example.com/track', JSON.stringify({ event: 'page_close' }));
});
4. 终极方案:Service Worker + 后台同步
如果数据非常重要(如支付日志),可使用 Service Worker 在后台发送数据,即使页面已关闭也能继续尝试。
代码示例
// 主线程:将数据存入 IndexedDB,由 Service Worker 处理
window.addEventListener('beforeunload', async () => {
const pendingData = ['event1', 'event2'];
await saveToIndexedDB(pendingData); // 存储到本地数据库
navigator.serviceWorker.ready.then(registration => {
registration.sync.register('send-pending-data');
});
});
// Service Worker 脚本(sw.js):
self.addEventListener('sync', event => {
if (event.tag === 'send-pending-data') {
event.waitUntil(sendPendingDataFromDB()); // 从 IndexedDB 读取并发送
}
});
- 优点
✅ 数据可靠性最高(支持离线重试)
- 缺点
❌ 实现复杂,需处理 Service Worker 生命周期
❌ 需要 HTTPS 环境
最佳实践总结
方案 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
sendBeacon | 大多数埋点场景 | 不阻塞页面,可靠发送 | 数据大小受限 |
同步 XHR | 兼容旧浏览器 | 确保请求发出 | 阻塞页面,可能被浏览器拦截 |
visibilitychange | 应对浏览器直接关闭 | 覆盖更多关闭场景 | 仍需配合 beforeunload |
Service Worker | 关键数据(如支付日志) | 离线支持,最高可靠性 | 实现复杂 |
推荐选择
- 现代浏览器:sendBeacon + visibilitychange(覆盖 99% 场景)。
- 关键数据:Service Worker 后备。
- 兼容旧浏览器:同步 XHR 降级(但需测试浏览器行为)。
补充:如何优化埋点数据积压?
- 定期批量发送:通过 setInterval 每 5~10 秒发送一次积压数据,减少卸载时的压力。
- 本地缓存:使用 localStorage 或 IndexedDB 存储未发送的数据,下次页面加载时重试。
- 减少数据量:压缩埋点数据(如使用 gzip),避免 Beacon 大小限制。