网页通常需要将数据(或“信标”)发回其服务器,例如,用户当前会话的分析数据。对于开发者来说,这需要权衡:减少可能多余的持续请求,同时又要确保在发送信标之前,如果标签页关闭或用户导航离开,不会错过数据。
传统上,开发者使用 pagehide
和 visibilitychange
事件捕获页面在卸载时的数据,然后使用 navigator.sendBeacon()
或 fetch()
与 keepalive
一起发送信标数据。不过,这两种事件都存在一些棘手的极端情况,这些情况因用户的浏览器而异,有时这些事件根本不会到达,尤其是在移动设备上。
fetchLater()
提议使用单个 API 调用来代替这种复杂性。它的作用与其名称完全相符:它会要求浏览器确保在未来某个时间点发出请求,即使网页关闭或用户离开也是如此。
fetchLater()
已在 Chrome 121 版(2024 年 1 月发布)中推出,可供用户在源试用后参与测试,该试用将持续到 2024 年 9 月 3 日。
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 对象使用超时 activateAfter
扩展了 fetch()
的选项,以便您在超时后或页面卸载时(以先到者为准)触发请求。
这样,您就可以决定是在可能的绝对最后时刻还是更及时地获取数据。
例如,如果您的应用通常会被用户在整个工作日内保持打开状态,您可能希望设置一小时的超时时间,以确保获得更精细的分析数据,同时在用户在该一小时内退出应用时仍能发送信标。然后,您可以为接下来一小时的分析设置新的 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);
每次收到指标更新时,系统都会使用 AbortController
取消所有现有的定期 fetchLater()
,并创建包含更新的新 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 上提交问题和反馈。