首页/文章/javascript

sendBeacon 了解一下

2025-06-05
298410 分钟
...

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

sendBeacon 是什么,为什么要用?

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

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

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

基本用法

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

navigator.sendBeacon(url, data);

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


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

document.addEventListener("visibilitychange", () => {
  if (document.visibilityState === "hidden") {
    
    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 能为用户带来更流畅的体验,也让我们开发者少操心很多。

如果您觉得这篇文章有帮助,请点个赞吧~

分享文章

相关文章

更多文章 →
javascript2025-09-18
JavaScript 的多线程能力:Worker
如果你写过一些计算量稍大的 JavaScript 代码,比如图像处理、大量数据排序或者复杂的算法,你几乎肯定遇到过浏览器“卡死”的现象。点击页面没反应,动画也停了,就像整个世界都静止了。 这就是主线程被阻塞的典型后果。因为主线程既要负责执行 JavaScript,又要负责渲染页面、响应用户操作,一旦它被繁重的计算任务占满,就无暇顾及其他,用户体验便直线下降。 这个问题的根源,正是“主线程是单线程的”。那么,如何解决呢? 答案很简单:把这...
学习面试
javascript2025-09-10
手把手教你实现一个EventEmitter
你是不是也遇到过这种情况:代码里各种事件监听和触发,回调函数套了一层又一层,最后自己都理不清哪个事件先触发、哪个后触发了? 别担心,今天我就带你从零开始实现一个自己的事件触发器(EventEmitter),让你彻底掌握事件驱动的精髓,代码清晰度直接提升200%! 为什么要懂EventEmitter? 先说说为什么我们要关心这个。Node.js的核心就是事件驱动,比如文件读写、网络请求,都是通过事件来处理的。不会EventEmitter,...
学习面试
javascript2025-08-09
Web Worker 多线程魔法
你是否曾遇到过网页因为复杂计算而卡顿的情况?今天,让我们一起探索Web Worker的神奇世界,看看如何用多线程技术解决性能瓶颈,并实现流畅的图片压缩功能! 为什么需要Web Worker? 想象一下,你在一个繁忙的餐厅里,只有一位服务员(主线程)负责点餐、上菜、结账所有工作。当客人突然暴增时,这位服务员就会手忙脚乱,其他客人只能干等着… 这就是JavaScript的现状—— 单线程 !所有任务都在一个线程中排队执行,一旦遇到复杂计算(...
学习面试
javascript2025-08-07
🌐AST抽象语法树
AST 是前端开发中一个非常重要的概念,它就像是代码的“编译器”,能够帮助我们理解和操作代码。本文将深入探讨 AST 的原理和应用,带你从零开始了解 AST。 一、什么是 AST? AST(抽象语法树)是一种树形结构,用于表示程序代码的语法结构。它将代码分解为一系列的节点,每个节点代表代码中的一个元素,如变量、函数、表达式等。AST 是编译器和解释器的基础,也是代码分析和转换的关键工具。 1\. AST 的结构 AST 的结构通常包括以...
学习面试
javascript2025-08-05
打字机效果是如何实现的
分析效果 使用 AI 对话的时候,文字是一个字一个字蹦出来的?它不是等所有内容生成完毕后一次性展示,而是以一种流畅、实时的“打字机”效果呈现,这种体验的背后,隐藏着一种高效的Web技术。它就是 服务器推送事件(Server Sent Events,简称 SSE) 什么是 SSE 从根本上说,SSE 是一种允许服务器 单向 、 持续 地向客户端(浏览器)推送数据的Web技术。 可以把它想象成一个 电台广播 : 服务器 是广播电台,持续不断...
学习面试
javascript2025-08-04
Promise 从原理到实现
前言 是 异步编程的核心概念之一,它为我们提供了一种更优雅的方式来处理异步操作。 本文将全面剖析 的工作原理,并逐步实现一个符合 规范的 。 正文 一、Promise 基础概念 代表一个异步操作的最终完成(或失败)及其结果值。它有 种状态: 1. pending : ,既不是成功,也不是失败状态 2. fulfilled :意味着操作 完成 3. rejected :意味着操作 状态一旦改变就不能再变,只能从 变为 ,或从 变为 。 二...
学习面试

评论

请登录后发表评论

去登录
加载评论中...

目录