不少开发者遇到过这样的问题:线上数据看板显示用户行为数据不全,明明功能已经上线,但收集到的数据却比预期少很多。经过排查发现,很多数据丢失发生在用户关闭页面或跳转离开的时候。原本通过fetch发送的请求,因为页面关闭而被浏览器强制取消,导致数据无法成功送达服务器。
问题并不在于代码逻辑,而是浏览器的运行机制决定的。当用户关闭标签页时,浏览器会立即停止当前页面的所有JavaScript执行,包括尚未完成的网络请求。这意味着,即使用户已经触发了数据上报,这些请求也可能在到达服务器前就被中断了。
从2014年开始,浏览器提供了一个专门的api来解决这个问题:navigator.sendBeacon()。这个API的设计目的就是在页面卸载时可靠地发送数据。
与fetch不同,sendBeacon不会等待服务器响应,也不会延迟页面的关闭过程。它会将请求放入浏览器的发送队列,然后在后台完成传输,即使JavaScript已经停止执行也不会影响发送。
对比一下两种写法的区别:
// 这种写法在页面关闭时可能失败
window.addEventListener('beforeunload', () => {
fetch('/analytics', {
method: 'POST',
body: JSON.stringify({ event: 'page_exit' })
}); // 可能被取消
});
// 使用sendBeacon更可靠
window.addEventListener('beforeunload', () => {
navigator.sendBeacon('/analytics',
JSON.stringify({ event: 'page_exit' })
); // 能够成功进入发送队列
});
当用户快速关闭页面或跳转到其他页面时,这两种方式的差异非常明显。浏览器通常会取消fetch请求以确保页面快速关闭,而sendBeacon是专门为此场景设计的。
实际测试数据显示,navigator.sendBeacon()在页面切换时的数据送达率达到95.8%,而传统的fetch()方法只有84.9%。在移动端,这个差距更加明显。由于移动设备上更激进的内存回收机制和应用后台运行限制,fetch的失败率更高,而sendBeacon则能保持较好的稳定性。
sendBeaconAPI特别适合处理小型但重要的数据上报:
更高的可靠性:请求进入浏览器原生队列,能够跨越页面卸载的窗口
不会阻塞页面:不会拖慢用户的导航体验
使用简单:不需要复杂的备用方案或庞大的埋点库
sendBeacon有两个主要限制:单次请求大小不能超过64KB,并且多个beacon请求共享这个大小限制。
// 正好64KB,可以正常发送
navigator.sendBeacon('/log', 'A'.repeat(65536));
// 超过限制,会返回false表示失败
navigator.sendBeacon('/log', 'A'.repeat(65537));
sendBeacon()返回一个布尔值,表示请求是否成功进入队列。建议总是检查这个返回值并提供备用方案:
const success = navigator.sendBeacon('/analytics', data);
if (!success) {
// 备用方案:使用fetch或拆分数据
fetch('/analytics', {
method: 'POST',
body: data
});
}
另一个需要注意的限制是sendBeacon不支持自定义请求头。当发送字符串数据时,浏览器会使用默认的text/plain Content-Type,服务器端需要相应处理。
如果需要发送JSON数据,可以使用Blob对象,但这会触发CORS预检请求:
const data = { event: 'click', count: 42 };
const blob = new Blob([JSON.stringify(data)], {
type: 'application/json'
});
navigator.sendBeacon('/analytics', blob);
// 注意:这会触发CORS预检请求
大多数团队选择在服务器端解析text/plain内容,以避免额外的预检请求开销。
除了页面退出埋点,sendBeacon还非常适合实时错误上报:
window.addEventListener('error', (event) => {
const errorData = {
message: event.message,
filename: event.filename,
line: event.lineno,
stack: event.error?.stack,
timestamp: Date.now()
};
navigator.sendBeacon('/errors', JSON.stringify(errorData));
});
对于用户交互追踪,sendBeacon也是很好的选择:
document.addEventListener('click', (event) => {
const clickData = {
element: event.target.tagName,
x: event.clientX,
y: event.clientY,
timestamp: Date.now()
};
navigator.sendBeacon('/interactions', JSON.stringify(clickData));
});
近年来,另一种解决方案变得越来越流行:使用fetch的keepalive选项。
fetch('/analytics', {
method: 'POST',
body: JSON.stringify(data),
keepalive: true,
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
}
});
这种方式既保持了Beacon在页面卸载期的可靠性,又保留了fetch的灵活性,包括自定义请求头、丰富的HTTP方法和对响应的处理能力。到2025年,主流浏览器都对keepalive提供了很好的支持。
选择navigator.sendBeacon()当:
需要轻量级的埋点或日志记录
处理页面退出或会话结束时的数据
进行错误上报和诊断
追求最大兼容性和最简单实现
选择fetch({ keepalive: true })当:
需要自定义请求头或认证信息
需要处理服务器响应或复杂HTTP配置
需要更通用的请求能力
sendBeaconAPI在现代浏览器中已有很好的支持,覆盖约92%的用户:
Chrome 39+
Firefox 31+
Safari 11.1+
Edge 14+
Internet Explorer不支持此API,但在2025年这已经不再是主要问题。为了最大程度的兼容性,可以添加特性检测和备用方案:
if (navigator.sendBeacon) {
navigator.sendBeacon('/log', data);
} else {
// 回退到XMLHttpRequest
const xhr = new XMLHttpRequest();
xhr.open('POST', '/log', false);
xhr.send(data);
}
navigator.sendBeacon()和fetch with keepalive彻底解决了页面关闭时数据丢失的问题。无论是用户快速关闭页面还是跳转到其他网站,重要的数据都能可靠地送达服务器。对于需要准确收集用户行为数据的应用来说,正确使用这些API是确保数据完整性的关键。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!
传统Ajax是利用XMLHttpRequest(XHR)发送请求获取数据,不注重分离的原则。而Fetch API是基于Promise设计,专为解决XHR问题而出现。fetch API看起来简单,却是js语法不断增强提高带来的改善。
fetch就是XMLHttpRequest的一种替代方案。如果有人问你,除了Ajax获取后台数据之外,还有没有其他的替代方案?这是你就可以回答,除了XMLHttpRequest对象来获取后台的数据之外,还可以使用一种更优的解决方案fetch。
当fetch遇到302状态码,会发生什么?fetch不能拦截302,浏览器会自动从302响应的头信息的重定向地址中取到数据。针对认证的情况,后端可以返回401状态码,让前端去检查返回的状态码并据此执行相应操作。
Fetch API 提供了一个获取资源的接口(包括跨域请求)。任何使用 过 XMLHttpRequest 的人都能轻松上手,但新的API提供了更强大和 灵活的功能集。Fetch 提供了对 Request 和 Response (以及其他与网络请求有关的)对象的通用定义。
fetch默认不携带cookie配置其 credentials 项,其有3个值:omit: 默认值,忽略cookie的发送;same-origin: 表示cookie只能同域发送,不能跨域发送;include: cookie既可以同域发送,也可以跨域发送
fetch:一个获取资源的接口,类似于ajax,是基于 Promise之上设计,旧版本IE 完全不支持,须借助 polyfill来兼容,提供了对 Request 和 Response 对象的通用定义
Fetch 提供了一个 JavaScript接口,用于访问和操纵HTTP管道的部分,例如请求和响应。它还提供了一个全局 fetch()方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。
Fetch API 提供了一个 JavaScript接口,用于访问和操纵HTTP管道的部分,例如请求和响应。它还提供了一个全局 fetch()方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源
最近遇到一个下载的需求,由于 url 参数太长(常用的下载方法 a 标签或者 location.href 的方法都是 get 请求,get 请求参数长度有限制),无法下载,考虑了好几种方案,最终还是觉得通过 ajax 的 POST 方法进行下载,比较容易实现
在 AJAX 时代,进行 API 等网络请求都是通过 XMLHttpRequest 或者封装后的框架进行网络请求。 现在产生的 fetch 框架简直就是为了提供更加强大、高效的网络请求而生,虽然在目前会有一点浏览器兼容的问题
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!