网页需要将数据(或“信标”)发送回服务器的情况十分常见,比如用户当前会话的分析数据。对于开发者而言,这需要进行权衡:减少持续性(可能冗余)的请求,而不会有丢失数据的风险(如果在可发送信标之前关闭标签页或用户已经离开)。
过去,开发者使用 pagehide
和 visibilitychange
事件在页面卸载时捕获网页,然后将 navigator.sendBeacon()
或 fetch()
与 keepalive
结合使用,以便信标数据。然而,这两种事件都存在困难的极端情况,具体取决于用户的浏览器,并且有时这些事件根本不会到达,尤其是在移动设备上。
fetchLater()
建议用单次 API 调用替代这种复杂性。顾名思义,它要求浏览器确保以后发出请求,即使页面已关闭或用户离开网页也是如此。
Chrome 中提供fetchLater()
可用于通过源试用从版本 121(2024 年 1 月发布)开始,一直运行到 Chrome 126(2024 年 7 月)之前的真实用户进行测试。
fetchLater()
API
const fetchLaterResult = fetchLater(request, options);
fetchLater()
采用两个参数,通常与 fetch()
的相同:
request
,字符串网址或Request
实例。- 可选的
options
对象,用于从fetch()
扩展options
,超时设置为activateAfter
。
fetchLater()
会返回一个 FetchLaterResult
,当前仅包含一个只读属性 activated
,当“后面的”已过并且提取操作完成时,该属性会设为 true
。对 fetchLater()
请求的任何响应都会被舍弃。
request
最简单的用法是单独使用网址:
fetchLater('/endpoint/');
但是,就像 fetch()
一样,您可以为 fetchLater()
请求设置大量选项,包括自定义标头、凭据行为、POST
正文和 AbortController
signal
(可能会取消请求)。
fetchLater('/endpoint/', {
method: 'GET',
cache: 'no-store',
mode: 'same-origin',
headers: {Authorization: 'SUPER_SECRET'},
});
options
如果您希望在超时后或页面卸载时(以先发生者为准)触发请求, options 对象会扩展 fetch()
的选项,并将超时值设为 activateAfter
。
这样,您就可以权衡利弊,即在绝对最后可能时刻或时间更及时获取数据。
例如,如果您的用户通常在整个工作日都保持打开状态,那么您可能需要将应用的超时时间设为一小时,以确保进行更精细的分析,同时保证用户在该小时结束之前的任何时间退出应用。然后,您便可以设置一个新的 fetchLater()
,进行下一个小时的分析。
const hourInMilliseconds = 60 * 60 * 1000;
fetchLater('/endpoint/', {activateAfter: hourInMilliseconds});
使用示例
在实际衡量核心网页指标时存在一个问题,即在用户实际离开网页之前,任何性能指标都可能会发生变化。例如,随时可能发生更大的布局偏移,或者网页可能需要更长的时间才能响应互动。
不过,您肯定不希望在网页加载时因存在错误或不完整的信标而丢失所有效果数据。它非常适合 fetchLater()
。
下例使用 web-vitals.js 库监控指标,并使用 fetchLater()
将结果报告给分析端点:
import {onCLS, onINP, onLCP} from 'web-vitals';
const queue = new Set();
let fetchLaterController;
let fetchLaterResult;
function updateQueue(metricUpdate) {
// If there was an already complete request for whatever
// reason, clear out the queue of already-sent updates.
if (fetchLaterResult?.activated) {
queue.clear();
}
queue.add(metricUpdate);
// JSON.stringify used here for simplicity and will likely include
// more data than you need. Replace with a preferred serialization.
const body = JSON.stringify([...queue]);
// Abort any existing `fetchLater()` and schedule a new one with
// the update included.
fetchLaterController?.abort();
fetchLaterController = new AbortController();
fetchLaterResult = fetchLater('/analytics', {
method: 'POST',
body,
signal: fetchLaterController.signal,
activateAfter: 60 * 60 * 1000, // Timeout to ensure timeliness.
});
}
onCLS(updateQueue);
onINP(updateQueue);
onLCP(updateQueue);
每次有指标更新时,任何现有的已安排 fetchLater()
都会通过 AbortController
取消,并且会创建一个包含更新的新 fetchLater()
。
试用 fetchLater()
如前所述,在 Chrome 126 之前,fetchLater()
处于源试用阶段。如需了解源试用的背景信息,请参阅“源试用使用入门”
对于本地测试,您可以使用 chrome://flags/#enable-experimental-web-platform-features
中的实验性 Web 平台功能标志启用 fetchLater
。您也可以在命令行中使用 --enable-experimental-web-platform-features
或更有针对性的 --enable-features=FetchLaterAPI
标志运行 Chrome,从而启用此功能。
如果您在公共网页上使用它,请确保在使用它之前检查是否已定义全局 fetchLater
,以便进行功能检测:
if (globalThis.fetchLater) {
// Set up beaconing using fetchLater().
// ...
}
反馈
开发者反馈对于正确使用新的 Web API 至关重要,因此请在 GitHub 上提交问题和反馈。