页面卸载时自定义上报请求被浏览器取消怎么办?
我在做页面性能监控时,想在页面关闭前上报用户停留时长。用了window.addEventListener('beforeunload')触发上报,但发现大部分请求都被浏览器取消了。尝试改用unload事件也不稳定,有时候数据完全没收到。
试过用AbortController控制请求,但发现页面关闭时信号量自动置为aborted。代码大致这样写的:
window.addEventListener('beforeunload', () => {
fetch('/log', {
method: 'POST',
body: JSON.stringify({ duration: Date.now() - startTime }),
headers: { 'Content-Type': 'application/json' }
});
});
控制台报错说请求因页面卸载被取消了。有没有更可靠的上报方式?听说navigator.sendBeacon能解决这个问题,但不太清楚具体怎么用。
你提到的
beforeunload和unload事件都不靠谱,现代浏览器的 bfcache 机制还会让这些事件根本不触发。真正推荐的做法是用navigator.sendBeacon(),这个 API 就是专门为这种场景设计的,它会把请求放入浏览器的发送队列,即使页面卸载了请求也能发出去。基本用法是这样的:
这里有几个细节要注意。第一,推荐监听
visibilitychange事件并判断hidden状态,而不是beforeunload,因为前者在移动端和现代浏览器的 bfcache 场景下更可靠,这是 Page Visibility Level 2 规范推荐的方式。第二,sendBeacon只支持 POST 请求,发送的是 POST body,服务端需要按 POST 方式接收。第三,如果需要自定义请求头,比如设置 Content-Type 为 application/json,可以用 Blob 包装:另外 fetch API 在较新的浏览器里支持
keepalive选项,功能和 sendBeacon 类似,也能保证请求在页面卸载后继续发送:keepalive: true的请求有 64KB 大小限制,超过会被浏览器拒绝,sendBeacon 也有同样的限制,不过对于监控上报来说完全够用。两种方式都可以,sendBeacon 兼容性更好一些,keepalive 写法更灵活,看你项目需要支持哪些浏览器吧。navigator.sendBeacon替代fetch,它专为页面卸载时可靠发送请求设计。修改你的代码如下:
确保后端用
Content-Type: application/json解析请求体,就这样。