页面离开时上报RRWEB事件到服务端的失效问题如何解决
页面卸载场景rrweb数据上报优化方案
核心问题修正
你当前的实现存在三个直接影响上报成功率的问题:
- 未校验
sendBeacon的返回值,当上报数据超过浏览器限制的64KB配额时,sendBeacon会直接返回false,请求不会被发送 - 触发上报后立即清空事件数组,异步上报逻辑可能还未读取到完整数据就被清空,导致上报内容为空
- 未给fetch请求添加
keepalive属性,这是fetch请求在页面卸载时被取消的核心原因
具体优化实现
1. 重构上报逻辑,添加降级兼容
优先用性能更好的sendBeacon,发送失败时自动降级为带keepalive属性的fetch,同时新增失败数据本地缓存补发逻辑:
function sendRecording() { // 先拷贝当前事件数据到临时变量,避免原数组清空影响上报内容 const currentEvents = [...sessionRecordingEvents] if (currentEvents.length === 0) return const params = new URLSearchParams({ key : 'data', values : JSON.stringify(currentEvents) }); // 优先调用sendBeacon const sendSuccess = navigator.sendBeacon(`${this.apiHost}/session`, params); if (!sendSuccess) { // 降级为带keepalive的fetch fetch(`${this.apiHost}/session`, { method: 'POST', body: params, keepalive: true, headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).catch(() => { // 请求失败时存入localStorage,下次页面加载时补发 localStorage.setItem('rrweb_unsent', JSON.stringify(currentEvents)) }) } }
2. 优化事件监听逻辑,添加防重复上报机制
同时新增页面启动时的本地缓存数据补发逻辑:
// 页面初始化时补发上次未发送的数据 const unsentData = localStorage.getItem('rrweb_unsent') if (unsentData) { const params = new URLSearchParams({ key : 'data', values : unsentData }); fetch(`${this.apiHost}/session`, { method: 'POST', body: params }).then(() => localStorage.removeItem('rrweb_unsent')) } // 上报锁,防止短时间重复触发上报 let isSending = false document.addEventListener("visibilitychange", function() { if (document.visibilityState === 'hidden') { if (isSending) return isSending = true sendRecording(); sessionRecordingEvents = [] // 300ms后重置锁,兼容用户切回页面的场景 setTimeout(() => isSending = false, 300) } else { startRecording(); } });
额外优化建议
- 平时采用增量上报策略,不要攒到页面卸载时一次性上报大量数据:每攒到10-20KB的rrweb事件就提前上报一次,页面卸载时仅上报最后剩余的小批量数据,避免超过64KB的配额限制
- 不要监听
unload事件,该事件在绝大多数移动端浏览器、Safari中不会被触发,visibilitychange已经可以覆盖所有页面切后台、关闭的场景 - 如果上报接口是跨域地址,需要提前配置好对应的CORS规则,否则带keepalive的fetch请求也会被拦截
内容的提问来源于stack exchange,提问作者user103485
相关产品推荐
相关产品推荐

