首页 飞机号购买自助下单官网内容详情

页面关闭时还在用同步请求?fetch keepalive 和 sendBeacon 让你优雅告别

2026-03-17 2 飞机号购买网站

在移动端用户迅速切换App之际,或者将浏览器标签予以关闭之时,会约有15%的用户行为数据,默默无闻地就此消失不见——这并非网络方面出现的问题,而是当页面卸载之际,异步请求被浏览器毫不留情地予以中断所导致的后果。在这些关键的时刻,怎样能够可靠地发送数据,这已然成为前端埋点必须要去攻克的技术难题。

页面卸载的残酷真相

首先一种为了优化资源释放以及加快页面关闭速度的浏览器,会主动终止所有跟当前页面关联的未完成网络请求。而后当用户于微信里打开网页后直接返回聊天列表时,或是在Safari中关闭标签页之际,那些仍在途中的fetch请求就会被立刻取消。尤其是在移动端,页面状态切换相当频繁,数据丢失率能够高达15%。

像要是采用传统方案运用beforeunload事件搭配同步XMLHttpRequest,诚然是能够强行去发送请求的,可不免会致使页面出现卡死状况,还会弹出确认框。谷歌Chrome从80版本起就已经开始逐渐弃用同步XHR了,类似这种牺牲用户体验的举措如今是不可取的。

第一把利器 Beacon API

诞生于2014年伴随Beacon API出现的navigator.sendBeacon()方法,是专门用来处理页面卸载之际的数据发送事宜的。它有两个可以接纳的参数,分别是目标URL以及数据主体,返回的是一个布尔值,这个布尔值用于表明浏览器是不是成功地把请求添加到发送队列之中。哪怕页面马上就关闭了,浏览器也会在后台竭尽全力去完成这个请求。

window.addEventListener('unload', (event) => {
  const data = JSON.stringify({ event: 'page_exit', time: Date.now() });
  navigator.sendBeacon('/log', data);
});

这个API存在明确的使用限制,其一,仅仅能够发送POST请求,其二,数据量通常是被限制在64KB以内的,其三,无法获取服务器响应。因为其设计目标是去发送少量分析数据,所以非常适宜用于记录页面停留时长、点击流等轻量级埋点。截止到2026年,移动端浏览器支持率已经达到96%以上。

fetch的keepalive选项

fetch API于后续规范里增添了keepalive选项,赋予了更具灵活性的页面卸载时数据发送能力,只要在fetch配置内添上{keepalive: true},浏览器便会把这个请求标记成独立于当前页面生命周期的,哪怕页面关闭也会于后台持续传输。

window.addEventListener('unload', () => {
  fetch('/log', {
    method: 'POST',
    body: JSON.stringify({ event: 'page_exit', time: Date.now() }),
    headers: { 'Content-Type': 'application/json' },
    keepalive: true
  });
});

针对sendBeacon而言,fetch keepalive与之相较,其所支持的是能够进行自定义的请求方法,以及可自定义的请求头,还有更为复杂的数据格式。而且,但其数据量所存在的限制却是跟Beacon一同共享同一个全局配额的,同样也是没办法获取响应结果的。另外,这一项功能是在Chrome 66以上版本才可以使用的,Safari则是从16.0开始予以支持的,覆盖范围是绝大多数的现代浏览器。

跨域与数据格式陷阱

在依靠 sendBeacon 去发送 JSON 数据之际,会碰到 Content-Type 被自动设定成 text/plain 的状况,而后端就得对应地去调整解析逻辑。要是执意要发送 application/json,那就非得运用 fetch keepalive 并且明确地设置请求头才行,然而此时有可能会触发 CORS 预检请求,进而增加请求失败的风险。

与当前页面不同源的目标服务器,如果想要避免请求被浏览器拦截,就需要服务器正确配置Access-Control-Allow-Origin和Access-Control-Allow-Methods响应头,因为无论是sendBeacon还是fetch keepalive,都必须遵守CORS规则,而跨域配置是另一个容易被忽视的坑。

实战中的避坑指南

切切不可于beforeunload事件里开展复杂操作或者进行大循环,这般会极为严重地拖慢页面卸载的速度。经过实际测试表明,超出50毫秒的同步任务便会使得用户显著地感觉到卡顿。与此同时,鉴于无法获取服务器响应,所有依赖结果的后置逻辑都得重新予以设计。

常见失败缘由是数据量超限,若单次上报超出64KB,可思量分块发送或者改用Service Worker方案,对于离线情形,能结合IndexedDB暂存钱数据,待网络恢复后再借由后台同步API发送。

浏览器兼容与降级方案

即便当下现代浏览器在支持程度方面表现良好,然而依旧得为那些老旧的浏览器预备降级的方案。当检测发觉不支持sendBeacon这种情形时,能够回退到运用图片打点的技术:去创建出一个Image对象,把数据编码到URL的查询参数当中。这样的一种方式尽管仅仅能够发送GET请求并且在长度上存在限制,不过其兼容性能够追溯至IE6。

对Safari浏览器的行为可得特别留意:它部分旧版本虽说支持sendBeacon,然而在PWA模式下有可能存在异常。建议在代码里添加特性检测以及错误捕获,当Beacon返回false时,把数据暂时存到localStorage,等页面下次加载时再补发。

function sendOnUnload(url, data) {
  if (navigator.sendBeacon) {
    navigator.sendBeacon(url, data);
  } else if (fetch && Request.prototype.hasOwnProperty('keepalive')) {
    fetch(url, { method: 'POST', body: data, keepalive: true });
  } else {
    // 最后的后备:同步 XHR(不推荐,但总比丢失好)
    const xhr = new XMLHttpRequest();
    xhr.open('POST', url, false);
    xhr.send(data);
  }
}

最后留下一道需要思考的问题:要是你的页面在进行上报数据之际,要从服务器那儿获取一个定制化的“再见”的消息用来展示弹窗,那该怎么去设计技术方面的方案呢?每天有一个问题:你在项目当中碰到过页面卸载的时候数据出现丢失的状况吗?欢迎在评论的区域分享你掉进坑的经历以及解决的方案。

页面关闭时还在用同步请求?fetch keepalive 和 sendBeacon 让你优雅告别

相关标签: # 前端开发 # 数据上报 # 页面卸载 # sendBeacon # fetchkeepalive