原文: Logging Activity With The Web Beacon API;
摘要
:Beacon API是一种将信息从网页记录回服务器的轻量级高效方法。 本文主要介绍如何使用它以及它与传统的Ajax技术的不同之处。
Beacon API是一个基于JavaScript的Web API,用于将少量数据从浏览器发送到Web服务器,而无需等待响应。 在本文中,我们将了解哪些内容可能有用,与XMLHTTPRequest('Ajax')等熟悉的技术的不同,以及如何开始使用它。
如果您已经知道为什么要使用Beacon,请随时直接跳到Getting Started部分。
Beacon API用于将少量数据发送到服务器,而无需等待响应。 最后一部分是最关键的,也是Beacon为何如此有用的关键 - 即使服务器发送响应,我们的代码也永远不会看到响应。 Beacons专门用于发送数据然后遗弃它。 我们不期待响应,也不会得到响应。
可以把它想象成度假时送回家的明信片。 你把一小部分数据放在上面(有点类似“希望你在这里”和“天气很不错”),把它放在邮箱里,你不期待回应。 没人发回回明信片说“是的,我希望我真的在那里,非常感谢你!”
对于现代网站和应用程序,有许多方案非常巧妙地融入了这种“发送 - 遗弃”模式。
大多数人想到的第一个示例是分析。 Google Analytics等大型解决方案可能会对页面访问等内容进行详细分析概述,但如果我们想要更加个性化的内容呢? 我们可以编写一些JavaScript来跟踪页面中发生的事情(可能是用户如何与组件交互,他们滚动到多远,或者在他们遵循CTA之前显示了哪些文章)。但我们需要在用户离开页面时发送该数据到服务器。 Beacon对于此是完美的解决方案,因为我们只是记录数据而不需要响应。
我们没有理由不能实现Google Analytics经常处理的那些平常的任务,报告用户自己以及他们的设备和浏览器的功能。 如果用户已登录会话,您甚至可以将这些统计信息与已知个人联系起来。 无论收集什么数据,都可以使用Beacon将其发送回服务器。
此行为的另一个有用的应用是从JavaScript代码中记录信息。 想象一下,您的页面上有一个复杂的交互式组件,可以完美地适用于所有测试,但偶尔会在生产环境中失败。 你知道它失败了,但是你无法看到错误,以便开始调试它。 如果可以检测到代码本身的故障,则可以收集诊断信息并使用Beacon将其全部发回以进行记录。
实际上,任何日志记录任务都可以使用Beacon执行,即在游戏中创建保存点,收集有关功能使用的信息,或记录多变量测试的结果。 如果它是你希望服务器知道的浏览器中发生的事情,那么Beacon可能是实现这些方案一个有力竞争者。
我知道你在想什么。 这些都不是新的,是吗? 十多年来,我们已经能够使用XMLHTTPRequest从浏览器与服务器进行通信。 最近我们还有Fetch API,它与更现代的基于promise的接口做了很多相同的事情。 鉴于此,为什么我们需要Beacon API呢?
这里的关键是因为我们不会得到响应,浏览器可以排队请求并发送它而不阻塞执行任何其他代码。 就浏览器而言,无论我们的代码是否仍在运行,或者脚本执行所在的位置都无关紧要,因为没有什么可以返回的,它可以等到方便的时候直接发送HTTP请求。
这可能意味着要等到CPU负载较低,或等到网络空闲,或者如果可以的话甚至直接马上发送它。 最重要的是浏览器会将beacon排队,并且会立即返回到页面的脚本控制。 beacon在发送的时候不会挂起浏览器其他的事情。
要理解为什么这是一个大问题,我们需要注意用什么方式以及在什么时候,从我们的代码发出这些类型的请求。 以我们的分析日志记录脚本为例。 我们的代码可能会计算用户在页面上花费的时间,因此在最后一刻将数据发送回服务器变得至关重要。 当用户离开页面时,我们想要停止计时并将数据发回服务器。
通常,您可以使用unload
或beforeunload
事件来执行日志记录。 当用户执行类似跟踪页面上的链接导航离开时,会触发这些操作。 这里的麻烦在于运行的代码中一个unload
事件的将会阻止脚本执行并延迟卸载页面。 如果页面的卸载被延迟,那么加载下一页也会延迟,因此体验感觉非常缓慢。
请记住HTTP请求的速度能有多慢就会有多慢。 如果您正在考虑性能,通常尝试减少额外的HTTP请求是主要影响因素之一,因为发出网络请求并获得响应可能会非常慢。 你要做的最后一件事就是减少在激活链接和下一页请求开始之间的时间差。
Beacon通过排队请求而不阻塞页面脚本执行解决这个问题,将控制权立即返回到您的脚本。 然后浏览器负责在后台发送该请求而不会阻塞。 这使得一切都变得更快,这让用户更快乐,让我们都能保住工作。
至此,我们已经了解了Beacon是什么,以及为什么我们可以使用它,所以让我们开始使用一些代码。 基础的使用更简单:
let result = navigator.sendBeacon(url, data);
result返回的结果是boolean类型,如果浏览器接受请求并将其排队,则为true;如果出现问题,则为false。
navigator.sendBeacon()
navigator.sendBeacon有两个参数。 第一个是发出请求的URL。 请求作为HTTP POST执行,发送第二个参数中提供的任何数据。
data参数可以是多种格式,可以是Fetch API
支持的所有格式。 这可以是Blob
,BufferSource
,FormData
或URLSearchParams
- 基本上可以是是使用Fetch发出请求时,使用的任何正文类型。
我喜欢使用FormData
作为基本键值数据,因为它简单易读。
// URL to send the data to 请求的地址
let url = '/api/my-endpoint';
// Create a new FormData and add a key/value pair 创建一个FormData,并添加键值对
let data = new FormData();
data.append('hello', 'world');
let result = navigator.sendBeacon(url, data);
if (result) {
console.log('Successfully queued!');
} else {
console.log('Failure.');
}
Beacon的浏览器支持非常好,唯一值得注意的例外是Internet Explorer(适用于Edge)和Opera Mini。 对于大多数用途,这应该没问题,但在尝试使用navigator.sendBeacon之前,需要测试是否支持。
简单的方式:
if (navigator.sendBeacon) {
// Beacon code 使用Beacon的方式
} else {
// No Beacon. Maybe fall back to XHR? 不支持,使用其他替代方案
}
如果Beacon不可用且您的请求很重要,您可以回退到会阻塞的方法,例如XHR。 根据您的受众和目的,您可能同样选择不打扰,不去发送。
为了在实践中看到这一点,让我们创建一个基礎的系统来计算用户在页面上停留的时间。 当页面加载时我们会记下时间,当用户离开页面时,我们会将开始时间和当前时间发送给服务器。
由于我们只关心花费的时间(而不是实际的时间),我们可以使用performance.now()
来获取页面加载时的基本时间戳:
let startTime = performance.now();
如果我们将日志记录包装到函数中,我们可以在页面卸载时调用它。
let logVisit = function() {
// Test that we have support 测试是否支持
if (!navigator.sendBeacon) return true;
// URL to send the data to, e.g. 请求的地址
let url = '/api/log-visit';
// Data to send 需要发送的数据
let data = new FormData();
data.append('start', startTime);
data.append('end', performance.now());
data.append('url', document.URL);
// Let's go! 发送
navigator.sendBeacon(url, data);
};
最后,我们需要在用户离开页面时调用此函数。 我的第一直觉是使用unload
事件,但Mac上的Safari似乎会用安全警告来阻止请求,所以在这里使用beforeunload
也挺好。
window.addEventListener('beforeunload', logVisit);
当页面卸载时(或者在此之前),我们的logVisit()
函数将被调用,并且如果浏览器支持Beacon API,我们的Beacon将被发送。
(注意,如果浏览器不支持Beacon,我们返回true并假装它运行良好。返回false将取消该事件并停止页面卸载。这将是不幸的。)
由于Beacon的许多潜在用途都围绕着行为的跟踪,我认为更不用说我们需要注意,作为开发人员在记录和跟踪可能与用户绑定的行为时所承担的社会和法律责任。
我们可能会想到最近的欧洲GDPR法律与电子邮件相关的内容,但当然,立法涉及存储任何类型的个人数据。 如果你知道你的用户是谁并且可以识别他们的会话,那么你应该检查你正在记录的行为以及它与您所声明的政策的关系。
通常我们不需要像开发人员告诉我们的那样追踪尽可能多的数据。 最好故意不存储能够识别用户的信息,然后减少出错的可能性。
除了法律要求之外,大多数浏览器还具有使用户能够表达不被跟踪的愿望的设置。 Do Not Track使用如下所示的请求发送HTTP标头:
DNT: 1
如果你正在记录跟踪特定用户,而用户发送的请求是头部信息包含DNT为正数的数据,那么最好遵循用户的意愿匿名化该数据或根本不跟踪它。
例如,在PHP中,您可以非常轻松地测试此头部信息,如下所示:
if (!empty($_SERVER['HTTP_DNT'])) {
// User does not wish to be tracked ... 用戶不希望被跟蹤
}
Beacon API是一种非常有用的方法,可以将数据从页面发送回服务器,尤其是在日志记录环境中。 浏览器支持非常广泛,它能够无缝地记录数据,而不会对用户的浏览体验和网站性能产生负面影响。 请求的非阻塞性质意味着性能比XHR和Fetch等替代方案快得多。
如果您想了解有关Beacon API的更多信息,请访问以下网站。
“W3C Beacon specification,” W3C Candidate Recommendation
“MDN Beacon documentation,” MDN web docs, Mozilla
“Browser support information,” caniuse.com
祝好。