你以为页面卸载时 AJAX 就够用了?错!sendBeacon 了解一下

在前端开发中,我们经常需要在用户离开页面时上报日志或统计数据。然而,传统的 AJAX 异步请求往往在页面卸载时被浏览器忽略,这意味着用户可能刚一关闭标签页,我们还没来得及把埋点数据发出去就失败了。为了解决这个问题,HTML5 引入了 navigator.sendBeacon() 方法。简单来说,这个方法可以让浏览器在页面即将卸载 时悄悄地把少量数据通过 POST 请求发送给服务器,而不会影响后续页面加载。

sendBeacon 是什么,为什么要用?

navigator.sendBeacon() 是浏览器提供的一个接口,用于在后台异步发送小量数据(比如统计信息、日志上报)到服务器。它的设计目标就是在用户离开当前页面(关闭标签页、跳转新页时)时,依然能可靠地把数据送出去,而不必使用阻塞性技术。换句话说,sendBeacon 让我们不用等服务器响应就把"信"丢给浏览器,由浏览器代劳投递。

打个比方:把普通的 AJAX 请求比作发快递------用户走的时候,你还要等快递公司回个电话确认投递;而 sendBeacon 更像是寄张明信片,你把信息写好投递箱里,浏览器会在后台把它送到目的地。这样做有几个好处:它不阻塞页面卸载 (用户关网页时不需要一直等),而且能尽量保证数据送达,不会像普通异步请求那样在页面关闭时被强行中断。

根据 MDN 文档介绍,sendBeacon 的特性很清晰:它可以保证数据可靠地 异步发送给服务器,并且"不会拖慢下一次导航"。也就是说,一旦调用了 sendBeacon,浏览器会在有空闲时段自动完成发送,而不必等待结果。这正是传统同步 XMLHttpRequest 或在 beforeunload 事件中发送 AJAX 无法轻易做到的。

基本用法

使用 sendBeacon 非常简单,就像调用一个普通函数。最基本的用法是:

javascript 复制代码
navigator.sendBeacon(url, data);

其中 url 是接收数据的服务器地址(可以是相对路径或绝对路径),data 则是要发送的内容。data 可以是字符串、ArrayBufferBlobFormDataURLSearchParams 等。比如,如果我们想在页面关闭时把统计信息发送到 /log,可以这样写:

javascript 复制代码
// 当页面切换到后台或即将关闭时发送统计数据
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    const analyticsData = JSON.stringify({ page: "home", time: Date.now() });
    navigator.sendBeacon("/log", analyticsData);
  }
});

代码运行后,当页面变为隐藏(如用户切换标签或关闭页面)时,浏览器会在后台异步发送一个 POST /log 请求。需要注意的是,sendBeacon 是异步非阻塞的,所以它不返回任何响应内容 。调用时,它会立即返回一个布尔值:true 表示数据已成功加入发送队列,false 表示加入队列失败(例如数据过大超出限制)。

sendBeacon 的优势

相比于旧式同步请求,sendBeacon 带来了几个重要优势,尤其是针对页面卸载场景:

  • 可靠性高:浏览器会尽量确保数据被发送出去,即使用户已经关闭或切换了页面。这意味着 sendBeacon 把数据放入后台队列后,即使新页面开始加载,浏览器也会默默完成发送任务。
  • 非阻塞:发送过程不会阻塞页面卸载。用户点击链接或关闭标签页时,不需要等到数据发送完毕,因此体验更流畅,不会出现"卡住页面"的感觉。
  • 简单易用:接口简单,只需提供目标 URL 和数据就行,不用处理服务器返回。这使得它特别适合只需要"fire-and-forget"(只管发送,不关心回应)的上报场景。
  • 数据自动带 Cookie:sendBeacon 默认会发送同站点 Cookie,不像某些图片或脚本方式需要额外处理,这对统计用户会话很方便(但要注意遵循同源策略或服务器的 CORS 设置)。

用一句话概括:sendBeacon 就像是给网站提供了一个专用的"后台邮筒",你把数据扔进去就行了,浏览器负责在合适的时候把它送到服务端。它的出现彻底避免了使用同步 XHR 造成的页面卡顿和无响应窗口等问题。

典型使用场景

最常见的场景是 在会话结束时上报数据 。例如我们想记录用户在页面停留时的行为统计或日志,那么最可靠的时机就是在用户离开页面时发送。MDN 建议把 sendBeacon 放在 visibilitychange 事件中触发,当 document.visibilityState === "hidden" 时进行上报。这个事件会在页面切换标签或浏览器最小化时触发,通常发生在页面卸载之前。相比之下,传统的 beforeunload/unload 事件在移动端浏览器中并不靠谱,有可能根本不会触发。

例如在文档里监听 visibilitychange

javascript 复制代码
document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    // 页面即将切换或关闭,此时用 sendBeacon 上报数据
    navigator.sendBeacon("/log", analyticsData);
  }
});

这样做可以最大程度保证在导航离开前把数据"寄出"。如果不支持 visibilitychange,也可以退而求其次使用 pagehide 事件。不过通常来说,我们只要在用户离开页面那一刻调用 sendBeacon 就行,不要试图让用户停留太久。

限制与注意事项

sendBeacon 并不是万能的,它有一些技术限制,需要在使用时注意:

  • 仅限 POST 请求sendBeacon 只会以 POST 方法发送请求,不支持 GET 或自定义 HTTP 方法。因此,如果你的接口必须用 GET 或需要自定义请求头,就不能用它。
  • 无法读取响应 :使用 sendBeacon 后,浏览器不会返回服务器的响应内容,我们也无法执行回调来处理结果。调用时只会得到一个布尔值告诉你数据是否已加入发送队列。这意味着如果数据发送失败(比如服务器报错),前端无从得知,只能通过后端日志或其他方式去排查。
  • 数据大小有限制 :虽然官方规范没有硬性规定最大长度,但实际浏览器会对发送数据的大小做限制。一般来说,单次发送的数据不要超过 64KiB (约 65536 字节),否则可能发送失败。如果超限,sendBeacon 会返回 false 并且不会发送数据,因此最好确保上报内容轻量,比如只发送必要的 JSON 信息。
  • 遵循同源策略 :默认情况下,sendBeacon 遵守同源策略。如果要跨域发送,需要服务器端设置正确的 CORS 头。否则有安全限制。
  • 不适合重要任务 :因为不能确认发送成功且不影响用户操作,sendBeacon 只适合用于次要的追踪或统计,不适合关键业务数据的提交。例如支付、表单提交等需要确认成功的场景,仍应使用常规的 AJAX 或其他机制。

此外,需要注意的是,某些老旧浏览器(如 IE)可能不支持此 API。但目前绝大多数现代浏览器都已支持 sendBeacon。我们可以通过简单的判断来兼容处理:如果不支持,就退回到其他上报方式。

总结一下主要限制,可以用以下要点概括:

  • 方法固定 :只能发 POST,无法改用其他 HTTP 方法;
  • 无响应 :不接收响应内容,只能根据返回的 true/false 大致判断是否排队成功;
  • 大小限制:负载上限约 64KiB,数据超过这个大小可能发送失败;
  • 慎用时机 :由于依赖页面卸载,不能保证万无一失地送达,适合非关键性数据;如果需要更灵活的请求,请考虑 fetchkeepalive 选项。

与其他方案对比

在发送"退出数据"这个场景下,我们之前常用的做法是:在 beforeunload 事件里发同步 XHR、或创建一个临时的<img> 标签来请求后台,或者干脆用一个 setTimeout 等待。但这些方式都有各自问题,比如同步请求会阻塞页面,劣化用户体验;创建图片只适用于简单 GET 请求;unload/beforeunload 事件在移动设备上可能根本不被触发。相比之下,sendBeacon 设计之初就是解决以上痛点,它尽量兼顾可靠性和性能。

另外,如果你需要在卸载页面时发送更大数据或者需要等待服务器响应,则可以考虑 fetchkeepalive 选项。也就是说,用 fetch(url, { method: 'POST', keepalive: true, body: data })。这种方式同样可以让请求在页面卸载后继续进行,但它不像 sendBeacon 那样受到 64KiB 的限制。不过,fetch keepalive 并非所有浏览器都支持,而且可能需要手动管理一些细节;sendBeacon 则是专门为分析上报而生的更简洁方案。

小结

总的来说,navigator.sendBeacon() 是一个高效、可靠的后端上报工具 ,非常适合在用户离开页面时发送统计数据或日志。它可以让浏览器在后台悄悄完成 HTTP POST 请求,而不会阻塞页面卸载,也不会让用户等待。使用时只要注意数据量不宜过大(64KiB 限制)并了解它没有回调的特点即可。在现代浏览器中,这个功能已经被广泛支持。对于需要"背地里"上报数据的场景,学会使用 sendBeacon 能为用户带来更流畅的体验,也让我们开发者少操心很多。

相关推荐
Dream耀3 分钟前
CSS选择器完全手册:精准控制网页样式的艺术
前端·css·html
wordbaby3 分钟前
React 19 亮点:让异步请求和数据变更也能用 Transition 管理!
前端·react.js
月亮慢慢圆4 分钟前
VUE3基础之Hooks
前端
我想说一句5 分钟前
CSS 基础知识小课堂:从“选择器”到“声明块”,带你玩转网页的时尚穿搭!
前端·javascript·面试
红衣信15 分钟前
深入浅出 CSS 基础:从概念到选择器实战
前端·css
饮茶三千17 分钟前
五分钟!带你开发一个 VS Code 插件,实现状态栏文案轮播效果
前端
GIS之路17 分钟前
OpenLayers 地图投影转换
前端
用户48183772080318 分钟前
css grid实现流体布局
前端
Hanbox19 分钟前
探探React-找一个好看的组件库HeroUI(原NextUI)
前端