JS-前端埋点神器 navigator.sendBeacon 全指南

发布时间:2026/7/2 4:52:00
JS-前端埋点神器 navigator.sendBeacon 全指南 前端开发中埋点系统是必不可少的一环。我们经常需要在用户关闭页面、刷新或跳转路由时向服务器发送最后一条统计数据比如用户停留时长、页面跳出率。但这看似简单的需求在实现时却危机四伏请求发不出去页面跳转卡顿今天我们就来聊聊这个问题的终极解决方案 ——navigator.sendBeacon。一、 痛点与传统方案的挣扎场景还原当用户点击关闭按钮时浏览器会触发生命周期事件unload或visibilitychange。如果我们直接使用普通的异步 AJAX (xhr或fetch) 发送请求浏览器通常会忽略它因为页面都要销毁了浏览器不想处理未完成的请求。传统方案同步 XHR为了保证数据能发出去以前的做法是将请求改为同步Synchronous。12345678910constsyncReport (url, { data {}, headers {} } {}) {constxhr newXMLHttpRequest();// 第三个参数 false 表示同步请求xhr.open(POST, url,false);xhr.withCredentials true;Object.keys(headers).forEach((key) {xhr.setRequestHeader(key, headers[key]);});xhr.send(JSON.stringify(data));};致命缺陷用户体验极差同步请求会阻塞主线程。这意味着只有请求发送完成页面才能关闭或跳转。在弱网环境下用户会感觉页面“卡死”了。浏览器废弃现代浏览器如 Chrome已经明确表示将在页面卸载期间禁用同步 XHR这种方法迟早失效。二、 救世主navigator.sendBeacon1. 概念navigator.sendBeacon()是专门为“页面卸载时发送数据”而设计的 Web API。 它的核心能力是将数据放入浏览器的发送队列即使页面已经关闭浏览器也会在后台默默完成发送。2. 核心优势可靠性高不受页面生命周期影响确保数据不丢失。非阻塞完全异步执行不会阻塞页面关闭或跳转用户体验丝滑。低优先级浏览器会择机发送通常是网络空闲时不争抢关键资源。3. API 语法1constresult navigator.sendBeacon(url, data);url请求地址。data要发送的数据支持ArrayBuffer、ArrayBufferView、Blob、DOMString、FormData或URLSearchParams。result返回值布尔值 (true/false)。true表示数据成功加入传输队列注意不代表服务器接收成功。false表示队列已满无法加入。三、 实战三种常见发送姿势1. 发送普通字符串默认Content-Type为text/plain。1234constreportData (url, data) {// data 可能会被转为字符串 [object Object]建议先 stringifynavigator.sendBeacon(url, JSON.stringify(data));};2. 发送 JSON 数据推荐如果你希望后端接收到的Content-Type是application/json或者application/x-www-form-urlencoded需要使用Blob来手动指定。1234567constreportData (url, data) {// ✅ 正确写法Blob 的第二个参数才是 optionsconstblob newBlob([JSON.stringify(data)], {type:application/json; charsetUTF-8// 或者 application/x-www-form-urlencoded});navigator.sendBeacon(url, blob);};3. 发送 FormData适用于需要上传文件或模拟表单提交的场景。浏览器会自动设置Content-Type为multipart/form-data。123456789101112constreportData (url, data) {constformData newFormData();Object.keys(data).forEach((key) {letvalue data[key];// FormData 的 value 只能是字符串或 Blobif(typeofvalue !string !(value instanceof Blob)) {value JSON.stringify(value);}formData.append(key, value);});navigator.sendBeacon(url, formData);};四、跨域场景的“万能钥匙” —— 1px 像素图片在某些场景下使用sendBeacon会有跨域问题而使用1px像素图片这种方式则利用了浏览器允许跨域加载资源如图片、脚本的特性绕过了复杂的 CORS 配置1. 核心原理通过动态创建Image对象将埋点数据通过URL Query的形式挂载在图片请求的地址后面。服务端在接收到请求后记录日志并返回一个 1x1 像素的透明图片。2. 代码实现12345678910111213141516171819202122/*** 跨域埋点发送1px 像素图片方案* param {string} url - 接口地址* param {Object} data - 埋点数据*/constreportByImg (url, data) {// 1. 构造查询参数字符串constparams Object.keys(data).map(key ${encodeURIComponent(key)}${encodeURIComponent(data[key])}).join();// 2. 创建图片实例constimg newImage();// 3. 监听回调可选用于监控发送是否成功img.onload () console.log(埋点发送成功);img.onerror (err) console.error(埋点发送失败, err);// 4. 添加时间戳用于确保每次请求都被视为独立的新资源避免被一些代理服务器或浏览器机制意外缓存constconnector url.includes(?) ?:?;img.src ${url}${connector}${params}_t${Date.now()};};3. 方案对比为什么不用其他方式4.注意事项URL 长度限制由于数据是带在 URL 上的浏览器对 URL 长度有限制通常为 2KB-8KB。如果数据量巨大请拆分发送或改用sendBeacon。GIF 是首选服务端推荐返回GIF格式。对比 PNG 和 JPGGIF 的透明像素块在文件头开销上是最小的仅 43 字节。内存释放在一些极端高频埋点场景下建议在onload之后执行img null彻底释放内存。五、 避坑指南面试考点请求类型固定sendBeacon只能发送POST请求。无法读取响应这是一个“射后不理”的 API你无法获取服务器返回的数据状态码、Response Body 等。数据大小限制虽然标准没有明确规定但浏览器对队列总大小有限制通常在 64KB 左右不适合发送大数据。Cookie 携带sendBeacon默认会携带同域的 Cookie。六、 面试模拟题Q1sendBeacon和ajax(XHR/Fetch) 有什么根本区别参考回答生命周期Ajax 请求属于页面上下文页面关闭时请求会被取消除非同步sendBeacon属于浏览器上下文页面关闭后依然存活。交互体验页面卸载时同步 Ajax 会阻塞跳转sendBeacon是异步非阻塞的。功能限制sendBeacon只能 POST无法自定义 headers除了 Content-Type且无法读取响应。Q2如果浏览器不支持sendBeacon怎么办参考回答需要做降级处理。检测navigator.sendBeacon是否存在。如果不存在降级为同步 XHR请求虽然体验差但得保数据。或者使用img标签发送 GET 请求仅限数据量极小且不需要响应的场景。Q3sendBeacon返回true代表数据一定发送成功了吗