一、Navigator.sendBeacon 是什么&能做什么?
Navigator.sendBeacon
是一个用于发送少量数据到服务器的 API
,尤其适用于在页面即将卸载时发送数据,如日志记录、用户行为分析等。
与传统的 AJAX
请求不同,sendBeacon
方法的设计目标是确保数据在页面卸载(例如用户关闭标签页或导航到新页面)时能够可靠地发送。
Navigator.sendBeacon
方法可用于通过 HTTP
POST
将少量数据异步传输到 Web
服务器。
它主要用于将统计数据发送到 Web
服务器,同时避免了用传统技术(如:XMLHttpRequest
)发送分析数据的一些问题。
二、Navigator.sendBeacon 详细介绍
1、使用场景
- 页面卸载时的日志记录:在用户离开页面时记录行为数据,如页面停留时间、点击行为等。
- 分析和监控:发送用户行为数据到分析服务器,用于网站性能监控和用户行为分析。
- 状态报告:向服务器报告应用程序状态或错误信息。
2、不使用 Navigator.sendBeacon 时,如何处理?
- 发起一个同步
XMLHttpRequest
来发送数据(open()
方法的第三个参数为false
)。 - 创建一个
<img>
元素并设置src
,大部分用户代理会延迟卸载(unload
)文档以加载图像。 - 创建一个几秒的
no-op
循环。
3、不使用 Navigator.sendBeacon 可能会带来哪些问题?
3.1. 同步 XHR
- 同步请求会阻塞浏览器的主线程,导致页面在请求完成之前无法响应用户操作。这会严重影响用户体验,尤其是在请求需要较长时间才能完成时。
- 许多现代浏览器会对同步请求发出警告,提示开发者这种方法可能会导致性能问题,并建议改用异步请求。
3.2. 创建 img
元素
- 页面卸载流程被阻塞。
- 后面页面的加载时机被延迟,用户体验不好。
3.3. 创建循环
- 页面卸载流程被阻塞。
- 后面页面的加载时机被延迟,用户体验不好。
4、Navigator.sendBeacon 的优点
- 可靠性:
sendBeacon
的主要设计目标是确保数据在页面卸载时能够可靠地发送。浏览器会在后台继续尝试发送数据,即使页面已经关闭或正在导航到新页面。 - 非阻塞:
sendBeacon
方法是非阻塞的,不会阻碍页面的卸载过程。相比于传统的同步AJAX
请求,不会影响用户体验或导致页面卸载延迟。 - 简单性:接口简单,只需提供目标
URL
和数据,无需处理响应。适用于只需要发送数据而不需要从服务器获取数据的场景。 - 安全性:与其他
AJAX
请求方法一样,sendBeacon
遵循同源策略,不能发送跨域请求,除非服务器设置了适当的CORS
头。
5、使用限制
- 数据大小限制(通常为几十
KB
),适用于发送少量数据。 - 因为数据大小有限,
sendBeacon
不适合用于发送大量或大文件的数据。 sendBeacon
方法始终使用HTTP POST
请求。- 不返回响应,不提供处理服务器响应的机制,无法检查请求是否成功以及服务器的返回结果。
6、浏览器兼容性
Navigator.sendBeacon
支持主流的现代浏览器,包括 Chrome
、Firefox
、Safari
、Edge
等,但不支持较老的浏览器(如 IE 11 及更早版本)。
三、Navigator.sendBeacon 如何使用
1、语法
navigator.sendBeacon(url); navigator.sendBeacon(url, data);
2、参数
2.1. url
url
参数表明 data
将要被发送到的网络地址。
2.2. data 可选
data
参数是将要发送的 ArrayBuffer
、ArrayBufferView
、Blob
、DOMString
、FormData
或 URLSearchParams
类型的数据。
3、返回值
类型:boolean
当用户代理成功把数据加入传输队列时,sendBeacon()
方法将会返回 true
,否则返回 false
。
4、Navigator.sendBeacon 使用举例
4.1. 参数格式为 ArrayBuffer
适用于传递二进制数据,如文件或图像片段。
const buffer = new ArrayBuffer(8); navigator.sendBeacon('/log', buffer);
4.2. 参数格式为 ArrayBufferView
ArrayBufferView
是一种表示二进制数据视图的类型。常见的 ArrayBufferView
类型包括 Uint8Array
, Int8Array
, Uint16Array
, Int16Array
, Uint32Array
, Int32Array
, Float32Array
, Float64Array
等等。
// 创建一个包含字符串的 Uint8Array const text = 'Hello, world!'; const textEncoder = new TextEncoder(); const textArray = textEncoder.encode(text); // 将字符串编码为 Uint8Array // 创建一个包含其他二进制数据的 Uint8Array const binaryData = new Uint8Array([1, 2, 3, 4, 5]); // 将两部分数据合并到一个大的 Uint8Array const combinedArray = new Uint8Array(textArray.length + binaryData.length); combinedArray.set(textArray, 0); combinedArray.set(binaryData, textArray.length); // 发送合并后的 Uint8Array navigator.sendBeacon('xxx/xxx', combinedArray);
4.3. 参数格式为 Blob
适用于传递文件或其他大块的二进制数据。
const blob = new Blob(['user=gqk'], { type: 'text/plain' }); navigator.sendBeacon('xxx/xxx', blob);
4.4. 参数格式为 DOMString
适用于简单的文本数据。
navigator.sendBeacon('xxx/xxx', 'this is sendBeacon case');
4.5. 参数格式为 FormData
适用于发送表单内容或复杂的键值对数据。
const formData = new FormData(); formData.append('user', 'gqk'); navigator.sendBeacon('xxx/xxx', formData);
4.6. 参数格式为 URLSearchParams
适用于发送 URL
编码的查询参数。
const params = new URLSearchParams(); params.append('user', 'gqk'); navigator.sendBeacon('xxx/xxx', params);
四、Navigator.sendBeacon 和 XHR、fetch 有什么异同点
1、相同点
- 发送网络请求:所有这三种方法都用于从客户端向服务器发送数据。
- 支持多种数据格式:它们都可以发送字符串、
JSON
、二进制数据等多种格式的数据。
2、不同点
2.1. Navigator.sendBeacon
- 用于在页面卸载时发送少量数据,适合日志、分析数据等用途。
- 发送数据是异步的,但不返回任何信息给调用者,无法处理服务器的响应。
- 设计为在页面卸载时保证数据发送,即使在浏览器关闭或页面跳转时。
- 始终使用
HTTP
POST
请求。 - 不需要处理响应,使用简单。
2.2. XMLHttpRequest
- 用于更复杂的、需要处理响应的
AJAX
请求。 - 可以进行同步或异步请求。
- 可以处理服务器的响应,并进行进一步处理(如解析
JSON
、处理状态码等)。 - 提供丰富的事件(如
onload
、onerror
、onprogress
等),可以监控请求的各个阶段。 - 在所有主流浏览器中都得到了广泛支持,包括一些较老的浏览器。
- 使用较复杂,需要处理请求的各个阶段和状态。
2.3. Fetch
- 现代化的请求接口,用于替代
XMLHttpRequest
,支持更简单和更灵活的请求和响应处理。 - 始终进行异步请求,返回
Promise
对象。 - 支持链式处理响应,可以轻松解析
JSON
、处理状态码等。 - 支持
async/await
语法,更符合现代JavaScript
开发习惯。 - 更好地支持跨域请求和
CORS
(跨域资源共享)。 - 允许在请求中添加更多的选项(如自定义头部、请求方法等)。
3、三者使用场景
Navigator.sendBeacon
:简单、适合在页面卸载时发送少量数据,不处理响应。XHR
:功能全面、适合复杂的AJAX
请求,但使用较复杂。Fetch
:现代化接口,简洁灵活,适合处理复杂请求和响应。
五、总结
Navigator.sendBeacon
是一个专为可靠性设计的API
,特别适用于在页面卸载时发送少量数据。- 它具有简单、非阻塞、可靠等优点,特别适合日志记录和用户行为分析等场景。
- 发出的是异步请求,并且是
POST
请求。 - 只能判断出是否放入浏览器任务队列,不能判断是否发送成功。
- 无需处理返回值。
- 需要注意浏览器兼容问题。