Web页面关闭时发送Ajax请求的几种方案及sendBeacon用法
本文详解在用户关闭或离开Web页面时发送数据上报的多种实现方式。对比了同步Ajax、服务端忽略Abort以及推荐的navigator.sendBeacon异步方案,重点解析sendBeacon的使用方法及Blob、FormData等数据格式的处理技巧,解决页面卸载时请求被中断的问题。
Web页面关闭时发送Ajax请求的几种方案及sendBeacon用法
前言
在某些业务场景下,我们需要在用户离开当前页面时执行数据上报以记录用户行为,或者通过Ajax请求通知服务器用户已离开,例如直播间内的退房操作。
本文将分两部分详细讲解如何实现退出行为的数据上报。
页面关闭事件监听机制
1. 事件监听机制
浏览器提供了两个关键事件用于监听页面关闭行为:beforeunload和unload。
beforeunload事件在文档和资源即将关闭时触发,此时文档仍可见,且该事件可被取消。例如,若采用如下代码写法,用户刷新或关闭页面时会弹出确认框,询问是否关闭页面。
window.addEventListener("beforeunload", function (event) {
// Cancel the event as stated by the standard.
event.preventDefault();
// Chrome requires returnValue to be set.
event.returnValue = '';
});
unload事件则在页面开始卸载时发生。此时文档状态表现为:1. 所有资源(如图片、iframe等)依然存在;2. 用户对资源不可见;3. 界面交互失效(window.open、alert、confirm等方法不可用);4. 错误不会阻止文档的卸载过程。
基于上述两个事件,我们可以实现对页面关闭的监听。为确保稳妥,建议同时监听这两个事件,并对监听函数进行封装,确保关闭逻辑仅执行一次。
页面卸载时的请求发送策略
2. 请求发送策略
仅完成事件监听是不够的。如果在监听函数中直接发送标准的Ajax请求,会发现请求会被浏览器强制中止(abort),导致无法成功发送。因为在页面卸载阶段,浏览器无法保证异步请求能够顺利完成。
针对这一问题,我们有以下几种解决方案:
方案一:发送同步Ajax请求
var oAjax = new XMLHttpRequest();
oAjax.open('POST', url + '/user/register', false);//false表示同步请求
oAjax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
oAjax.onreadystatechange = function() {
if (oAjax.readyState == 4 && oAjax.status == 200) {
var data = JSON.parse(oAjax.responseText);
} else {
console.log(oAjax);
}
};
oAjax.send('a=1&b=2');
该方法虽然有效,但用户必须等待请求结束才能关闭页面,严重影响用户体验。
方案二:发送异步请求,并在服务端忽略Ajax的中止(abort)
尽管异步请求会被浏览器中止,但如果服务端能够忽略中止操作并继续执行,则方案可行。例如,PHP提供了ignore_user_abort函数来处理此类情况。但这需要修改后台代码,通常实施成本较高,可行性较低。
方案三:使用navigator.sendBeacon发送异步请求
根据MDN文档介绍:
该方法主要满足统计和诊断代码的需求,这些代码通常尝试在文档卸载前向Web服务器发送数据。过早发送数据可能导致数据收集遗漏。然而,对开发者而言,保证在文档卸载期间发送数据一直是一个难点,因为用户代理通常会忽略在卸载事件处理器中产生的异步XMLHttpRequest。
从上述介绍可以看出,sendBeacon正是为解决用户离开时发送请求的场景而设计,非常适合此类需求。
其基本使用方式如下:
navigator.sendBeacon(url [, data]);
sendBeacon支持发送的数据类型包括ArrayBufferView、Blob、DOMString或FormData。
以下是几种使用sendBeacon发送请求的方式。我们可以修改Header和内容格式,但由于与服务器的通信协议通常固定,若随意修改Header或内容格式,服务器可能无法正确识别。
(1)使用Blob发送数据
使用Blob发送的优势在于可以自定义内容格式和Header。例如,通过以下设置方式,可将content-type设置为application/x-www-form-urlencoded。
blob = new Blob([`room_id=123`], {type : 'application/x-www-form-urlencoded'});
navigator.sendBeacon("/cgi-bin/leave_room", blob);

(2)使用FormData对象,此时content-type会自动设置为"multipart/form-data"。
var fd = new FormData();
fd.append('room_id', 123);
navigator.sendBeacon("/cgi-bin/leave_room", fd);

(3)数据也可以使用URLSearchParams对象,此时content-type会被设置为"text/plain;charset=UTF-8"。
var params = new URLSearchParams({ room_id: 123 })
navigator.sendBeacon("/cgi-bin/leave_room", params);

经测试发现,使用Blob发送较为便捷,内容设置也更具灵活性。若发送的消息经抓包后发现后台未识别,可尝试修改内容的字符串格式或Header,以找到合适的发送方式。 参考链接:
- sendBeacon API not working temporarily due to security issue, any workaround?
- Sending AJAX Data when User Moves Away / Exits from Page
- Setting HTTP Headers in a Beacon Request
总结
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对本站的支持。
为什么叫JavaScript:语言命名由来及与Java的历史渊源
2026-11-05 11:37:48
JS获取当前时间年月日时分秒并格式化为YYYY-MM-DD HH:mm:ss
2026-11-04 21:25:21
不同语言间JS与Java/Python加密结果不一致怎么办
2026-11-04 18:27:23
Mock.js 拦截 Axios 和 Fetch 请求的原理与配置方法
2026-11-04 08:13:59
JS怎么获取当前日期并格式化为YYYY-MM-DD
2026-11-03 18:47:03
