navigator.sendBeacon()
是HTML5新增的一个API,用于在页面卸载(如关闭、刷新、跳转)时异步发送少量数据到服务器,且不会阻塞页面卸载或影响后续导航的加载性能。这在需要收集用户行为数据(如页面停留时间、按钮点击统计)的场景中特别有用。
一、核心特点
- 异步非阻塞:数据发送不会阻塞页面卸载流程,用户体验不受影响。
- 高可靠性:使用HTTP POST请求,浏览器确保在页面关闭前尝试发送数据。
- 小数据量优化:设计初衷是发送少量数据(通常<64KB),过大的数据可能被丢弃。
- 跨域支持:支持跨域请求,需服务器配置CORS。
二、基本语法
javascript
const isSent = navigator.sendBeacon(url, data);
- 参数 :
url
:目标URL(字符串)。data
(可选):要发送的数据,可以是:ArrayBuffer
ArrayBufferView
(如Uint8Array
)Blob
/File
FormData
- 字符串(如JSON、普通文本)
- 返回值 :
true
:浏览器已将请求加入发送队列。false
:队列已满或请求无法入队(如URL无效)。
三、典型应用场景
-
埋点统计:
javascriptwindow.addEventListener('unload', () => { const data = JSON.stringify({ page: window.location.pathname, 停留时间: performance.now(), 点击事件: [...document.querySelectorAll('button')].map(btn => btn.id) }); navigator.sendBeacon('/analytics', data); });
-
用户会话追踪:
javascriptdocument.addEventListener('visibilitychange', () => { if (document.visibilityState === 'hidden') { navigator.sendBeacon('/session-tracker', 'session-ended'); } });
-
表单自动保存:
javascriptconst formData = new FormData(document.getElementById('myForm')); window.addEventListener('beforeunload', () => { navigator.sendBeacon('/auto-save', formData); });
四、与其他请求方式的对比
特性 | sendBeacon |
XMLHttpRequest /fetch |
---|---|---|
阻塞页面卸载 | ❌ 不阻塞 | ✅ 可能阻塞(同步请求) |
请求优先级 | 低(后台排队) | 高(可能影响新页面加载) |
数据大小限制 | 通常<64KB | 无明确限制 |
自动重试 | ✅ 浏览器保证尝试发送 | ❌ 需手动实现重试逻辑 |
适合场景 | 页面卸载时的小数据 | 实时数据交互 |
五、兼容性与替代方案
-
兼容性 :
主流浏览器(Chrome 39+、Firefox 31+、Safari 11+、Edge 14+)均支持,IE不支持。
-
降级方案 :
对于不支持的浏览器,可使用同步
XMLHttpRequest
(但会阻塞页面):javascriptfunction sendData(url, data) { if (navigator.sendBeacon) { return navigator.sendBeacon(url, data); } else { // 降级方案(注意:会阻塞页面) const xhr = new XMLHttpRequest(); xhr.open('POST', url, false); // 第三个参数为false表示同步请求 xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(data); return xhr.status === 200; } }
六、注意事项
-
数据格式:
- 发送JSON时需设置
Content-Type
(服务器端接收时需注意解析)。 - 发送
FormData
时无需手动设置Content-Type
,浏览器会自动处理。
- 发送JSON时需设置
-
服务器响应 :
sendBeacon
不关心服务器响应,无法获取返回值。 -
调试技巧:
- 在Chrome开发者工具的"Network"面板勾选"Preserve log"以查看卸载时的请求。
- 使用
visibilitychange
事件代替unload
/beforeunload
,因为前者在页面进入后台(如切换标签)时触发,调试更方便。
七、面试延伸问题
-
为什么
sendBeacon
比fetch
更适合页面卸载时的数据发送?→
fetch
默认是异步的,但页面卸载可能中断异步请求;而sendBeacon
由浏览器保证在页面关闭前尝试发送。 -
如果需要发送大量数据(如1MB),
sendBeacon
是否合适?→ 不合适,
sendBeacon
设计用于小数据,大数据可能被丢弃,建议使用fetch
配合keepalive
选项。 -
如何测试
sendBeacon
是否正常工作?→ 可使用
visibilitychange
事件触发发送,在开发者工具中观察网络请求;或在服务器端记录接收情况。
八、现代替代方案(fetch + keepalive)
对于需要发送大量数据或获取响应的场景,可使用fetch
的keepalive
选项:
javascript
window.addEventListener('unload', async () => {
const data = await generateLargeData();
await fetch('/api/upload', {
method: 'POST',
body: data,
keepalive: true // 即使页面关闭也继续发送
});
});
keepalive
的兼容性与sendBeacon
类似,但支持更大数据量和响应处理。