一、Navigator.sendBeacon 简介
(一)基本概念
Navigator.sendBeacon 是一个非常实用的 Web API,它主要用于在浏览器后台发送异步请求,尤其擅长在页面卸载或关闭这样的特定场景下,将数据可靠地发送至服务器,而且不会等待服务端的响应,也就不会阻塞页面的卸载或关闭流程。
比如说,当用户关闭浏览器标签页或者导航到其他页面时,可能有一些数据(像页面停留时间、用户行为记录等统计数据)需要发送给服务器进行分析和记录,Navigator.sendBeacon 就能很好地完成这个任务。
与我们常见的 Ajax 请求不同,Ajax 请求更多的是用于一般性的、需要获取服务器响应并进行相应交互的场景,而且在页面卸载等情况下,可能会出现请求未完成而导致数据丢失的问题。而 Navigator.sendBeacon 的设计目标就是确保数据在页面卸载这类关键时刻能够可靠地发送出去,它的优先级相对较低,会在浏览器的空闲时间去执行发送请求的操作,不会和其他高优先级任务(比如同步的 XMLHttpRequest 和 Fetch 请求等)争抢资源,更不会阻碍页面的正常卸载或者跳转过程。
此外,Navigator.sendBeacon 还有一个优势就是它不受跨域限制(当然前提是服务器端做好了相应的配置等情况),这使得它在数据发送方面更加灵活,能满足更多不同场景下的数据上报需求。
(二)语法及参数说明
Navigator.sendBeacon 方法的语法格式为 navigator.sendBeacon(url, data); 。
其中,url 参数是必不可少的,它代表着要发送请求的目标地址,可以是相对路径,例如 ./api/sendData,也可以是绝对 URL,像 example.com/api/log 等。
data 参数则是要发送的数据,它可以是多种类型,例如 Blob 类型,适合传递文件或者其他大块的二进制数据,以下是使用 Blob 类型数据的示例代码:
const blob = new Blob(['user=gqk'], { type: 'text/plain' });
navigator.sendBeacon('https://example.com/api', blob);
BufferSource 也是可发送的数据类型之一,常用于传递一些底层的二进制数据缓冲区内容。
FormData 类型就很适合发送表单内容或者复杂的键值对数据,示例如下:
const formData = new FormData();
formData.append('username', 'John');
formData.append('age', '30');
navigator.sendBeacon('https://example.com/submitForm', formData);
另外,还可以直接发送字符串类型的数据,比如简单的文本信息:
const message = 'This is a test message';
navigator.sendBeacon('https://example.com/message', message);
总之,根据实际的数据需求,选择合适的数据类型按照上述语法格式进行调用,就能利用 Navigator.sendBeacon 完成数据的发送了。
二、使用 Navigator.sendBeacon 的优势
(一)异步发送不阻塞页面
Navigator.sendBeacon 最大的优势之一就是其异步发送数据的特性,并且不会阻塞页面的卸载或跳转过程。在实际的网页应用场景中,当用户选择离开当前页面(比如关闭浏览器标签页、导航到其他页面等情况)时,往往还有一些重要的数据需要发送给服务器,像页面停留时间、用户行为记录、操作产生的日志信息等。
如果采用常规的异步请求方式,例如在 unload 或者 beforeunload 事件中使用 XMLHttpRequest 来发送这些数据,就可能会面临请求未完成但页面已经卸载,进而导致数据丢失的尴尬局面。而 Navigator.sendBeacon 则不同,它在后台异步执行数据发送任务,即使页面已经开始卸载了,它依然能够在浏览器的后台线程中默默进行数据发送操作,不会去强行占用页面卸载或跳转的时间,保证了页面卸载过程可以顺利进行,极大地提升了用户体验。
(二)更高的发送成功率
在页面卸载(如 unload 或 beforeunload 事件发生时)的场景下,对比使用 XMLHttpRequest 发送数据的方式,Navigator.sendBeacon 有着更高的发送成功率。
当我们使用 XMLHttpRequest 在页面卸载阶段发送数据时,由于浏览器此时正忙于处理页面卸载相关的一系列操作,很可能会终止正在进行的网络请求,这种竞争条件就容易导致数据丢失,发送失败的情况时有发生。
而 Navigator.sendBeacon 方法被浏览器优先处理,它能够巧妙地避开这些可能导致数据丢失的竞争情况。浏览器会尽力确保通过 Navigator.sendBeacon 发送的数据能够可靠地发往服务器,所以在页面卸载等关键时刻,它更有可能成功地把数据发送出去,为数据的可靠传输提供了有力保障。
三、Navigator.sendBeacon 的应用场景
(一)日志和分析数据收集
在实际的网站运营与开发中,收集各类分析数据对于了解网站使用情况、把握用户行为模式等方面起着至关重要的作用。而 Navigator.sendBeacon 在日志和分析数据收集场景中有着出色的表现。
传统的 AJAX 请求在这种页面即将卸载的情况下,很可能因为请求未完成而导致数据丢失,毕竟页面卸载流程不会等待 AJAX 请求结束。但 Navigator.sendBeacon 不同,它在后台异步发送数据,不会阻塞页面卸载过程,所以能可靠地将这些日志和分析数据发送至服务器,为后续基于数据分析进行网站优化、提升用户体验等工作提供有力的数据支撑。
(二)用户行为跟踪
理解用户行为模式对于网站运营者和开发者来说意义重大,它能够助力精准营销、优化网站交互设计等工作,而 Navigator.sendBeacon 可以很好地帮助我们跟踪用户的行为。
四、使用 Navigator.sendBeacon 的注意事项
(一)数据大小限制
不同浏览器对 Navigator.sendBeacon 发送的数据大小有着不同限制,一般来说,理论上虽可发送任意 MIME 类型,但建议将数据控制在几十 KB 内,比如通常要控制在 64KB 以下。如果发送的数据超出了这个限制,可能会导致数据无法被缓存,并立即触发 “failed to queue data for sending” 错误,进而致使发送失败。所以开发者在使用 Navigator.sendBeacon 时,一定要留意所发送数据的量,提前做好数据量的把控,避免因数据过大而影响发送效果。
(二)同源策略限制
Navigator.sendBeacon 这个方法遵循同源策略,意味着它只能向同源的目标 URL 发送数据。同源指的是协议、域名、端口都相同,如果目标 URL 与当前页面的源不同,就可能出现发送失败的情况。
(三)浏览器兼容性
Navigator.sendBeacon 在各主流浏览器中的支持情况有所不同。像 Chrome 42+、Firefox 42+、IE 10+、Edge 12+、Safari 6 + 等浏览器都对其提供了支持,不过较老的浏览器(如 IE 11 及更早版本)可能并不支持。
(四)无法获取服务器响应
由于 Navigator.sendBeacon 是异步发送数据的,并且它不会等待服务器的响应,所以开发者不能直接得知请求是否成功发送并被服务器接收。我们只能根据其返回值判断数据是否进入发送队列,返回 true 表示数据被异步缓存并将在页面卸载后发送,但这并不保证数据已被成功发送或接收,返回 false 则表示数据无法被缓存,通常是因为数据太大或 URL 无效等原因。正因为如此,该方法更适用于像日志记录和数据追踪这类不需要立即得到服务器响应的场景,开发者在使用时要结合实际需求,明确它的这一特性带来的影响。
五、Navigator.sendBeacon 示例及实践建议
(一)代码示例展示
以下是几个不同数据类型使用 Navigator.sendBeacon 发送数据的示例代码,帮助大家更好地理解其实际使用流程。
1. 发送对象类型数据
假设我们要发送用户的基本信息对象到服务器,示例代码如下:
const userInfo = {
userId: 1001,
userName: 'Alice',
age: 25
};
const url = 'https://example.com/userData';
// 将对象转换为JSON字符串进行发送,因为一般推荐使用JSON格式便于后端解析
const data = JSON.stringify(userInfo);
if (navigator.sendBeacon(url, data)) {
console.log('用户信息数据发送成功,已进入发送队列');
} else {
console.log('用户信息数据发送失败,未能进入发送队列');
}
2. 发送数组类型数据
比如要发送一组商品的编号数组给服务器,代码可以这样写:
const productIds = [101, 102, 103];
const url = 'https://example.com/productIds';
const data = JSON.stringify(productIds);
const result = navigator.sendBeacon(url, data);
if (result) {
console.log('商品编号数组数据发送成功,等待后台发送');
} else {
console.log('商品编号数组数据发送失败');
}
3. 发送字符串类型数据
当只需要简单发送一段文本信息时,示例如下:
const message = '这是一条测试消息';
const url = 'https://example.com/message';
const result = navigator.sendBeacon(url, message);
if (result) {
console.log('文本消息发送成功,已加入发送队列');
} else {
console.log('文本消息发送失败');
}
4. 发送 FormData 类型数据(常用于表单相关数据)
例如有一个简单的登录表单数据需要发送:
const formData = new FormData();
formData.append('username', 'Bob');
formData.append('password', '123456');
const url = 'https://example.com/login';
const result = navigator.sendBeacon(url, formData);
if (result) {
console.log('登录表单数据发送成功,进入发送队列');
} else {
console.log('登录表单数据发送失败');
}
在实际应用中,我们还可能需要对发送的数据大小进行判断,避免超出浏览器限制而导致发送失败。以下是一个简单的示例,检查数据大小是否合适再进行发送:
function checkAndSendData(url, data) {
const dataSizeInBytes = new Blob([data]).size;
const maxSize = 64 * 1024; // 假设以64KB为最大限制,实际中不同浏览器有差异
if (dataSizeInBytes <= maxSize) {
return navigator.sendBeacon(url, data);
} else {
console.log('数据大小超出限制,发送失败');
return false;
}
}
const largeData = new Array(100000).join('a'); // 模拟较大数据
const url = 'https://example.com/largeData';
const result = checkAndSendData(url, largeData);
if (result) {
console.log('数据大小符合要求,发送成功');
}
同时,根据 Navigator.sendBeacon 的返回值来处理发送结果也是很重要的操作,像上面的示例代码中,返回 true 表示数据被异步缓存并将在页面卸载后发送,但不保证最终一定成功发送和被服务器接收,返回 false 则往往意味着数据因某些原因(如太大或 URL 无效等)无法被缓存,需要开发者根据具体情况做相应处理。
(二)实践建议汇总
在实际项目中使用 Navigator.sendBeacon 时,遵循一些最佳实践能够让数据发送更加可靠和高效,以下是一些实用的建议汇总。
1. 推荐使用的数据格式
- JSON 格式优先:JSON 格式的数据在前后端交互中非常方便,后端语言大多都有成熟的库来解析 JSON 数据,所以在使用 Navigator.sendBeacon 发送数据时,建议将对象、数组等类型的数据先通过 JSON.stringify 方法转换为 JSON 字符串再发送。例如:
const userActivity = {
action: 'click',
target: 'button',
timestamp: Date.now()
};
const url = 'https://example.com/activityLog';
const data = JSON.stringify(userActivity);
navigator.sendBeacon(url, data);
这样后端接收到数据后可以很容易地解析并进行相应的处理,比如存储到数据库用于后续的用户行为分析等操作。
2. 错误处理方式
- 检查返回值判断入队情况:由于 Navigator.sendBeacon 不会等待服务器响应,我们主要通过其返回的布尔值来判断数据是否成功加入发送队列。如前面代码示例中多次展示的那样,返回 true 时表示数据已被异步缓存等待后续发送,返回 false 就需要排查原因,可能是数据超出了浏览器允许的大小限制,或者目标 URL 无效等情况导致的。例如:
const data = {
// 假设这里是要发送的数据
};
const url = 'https://example.com/someData';
const sendResult = navigator.sendBeacon(url, data);
if (sendResult) {
console.log('数据已成功加入发送队列,大概率会在页面卸载后发送');
} else {
console.log('数据加入发送队列失败,需检查数据大小、URL等问题');
}
- 结合浏览器兼容性做兜底处理:要清楚 Navigator.sendBeacon 在不同浏览器中的支持情况有所不同,对于那些不支持该方法的浏览器(比如较老版本的 IE 等),需要准备好备用方案来确保数据能正常发送。常见的做法是通过条件判断,在不支持 Navigator.sendBeacon 时使用其他方式(如同步的 XMLHttpRequest 请求等)来替代发送数据,示例代码如下:
if (navigator.sendBeacon) {
const data = {
// 数据内容
};
const url = 'https://example.com/data';
navigator.sendBeacon(url, data);
} else {
// 使用XMLHttpRequest作为备用方案发送数据
const xhr = new XMLHttpRequest();
xhr.open('POST', url, false);
xhr.send(JSON.stringify(data));
}
总之,在实际项目里运用 Navigator.sendBeacon 时,综合考虑数据格式、错误处理以及浏览器兼容性等方面的实践建议,能够更好地发挥它在页面卸载等场景下可靠发送数据的优势,满足项目中诸如日志记录、用户行为跟踪等各种数据上报的需求。