改进同步 XMLHttpRequest() 中的页面关闭

减少延迟导航

Joe Medley
Joe Medley

当用户关闭网页或应用时,系统通常会有尚未提交的分析数据或其他数据。为了防止数据丢失,某些网站会使用对 XMLHttpRequest() 的同步调用来使网页或应用保持打开状态,直到其数据传递到服务器为止。不仅有更好的方法来保存数据,而且这种技术还会延迟页面关闭长达数秒,导致糟糕的用户体验。

这种做法需要改变,并且浏览器正在响应。XMLHttpRequest() 规范已计划废弃和移除。Chrome 80 采取了第一步,即禁止在多个事件处理脚本内(具体而言,是 beforeunloadunloadpagehidevisibilitychange)在关闭过程中触发这些事件处理脚本时进行同步调用。WebKit 最近也收到了一项实现相同行为变更的提交

在本文中,我将为需要时间更新网站的用户简要介绍选项,并概述 XMLHttpRequest() 的替代方案。

暂时拒绝

Chrome 并不只是想拔下 XMLHttpRequest() 插头,因此提供了几个临时的停用选项。对于互联网上的网站,可以进行源试用。通过这种方式,您可以向页面标头添加特定于源站的令牌,以启用同步 XMLHttpRequest() 调用。此选项将在 Chrome 89 发布前不久(即 2021 年 3 月的某个时间)结束。Chrome 企业版客户还可以使用 AllowSyncXHRInPageDismissal 政策标志,该标志同时结束。

替代方案

无论您如何发送数据回服务器,最好都不要等到页面卸载后才一次性发送所有数据。除了会造成糟糕的用户体验之外,在新型浏览器中卸载还不可靠,如果出现问题,有可能导致数据丢失。具体而言,卸载事件通常不会在移动浏览器上触发,因为在不触发 unload 事件的情况下,可以通过多种方式关闭移动操作系统上的标签页或浏览器。通过 XMLHttpRequest(),可以选择使用较小的载荷。现在,这是一项要求按照规范要求,这两种替代方案的上传大小上限为每个上下文 64 KB。

提取 keepalive

Fetch API 提供了一种处理服务器交互的可靠方法,并提供用于不同平台 API 的一致的接口。其中的选项包括 keepalive,它可确保无论使请求进入的页面是否保持打开状态,请求都会继续执行:

window.addEventListener('unload', {
  fetch('/siteAnalytics', {
    method: 'POST',
    body: getStatistics(),
    keepalive: true
  });
}

fetch() 方法的优势在于可以更好地控制发送到服务器的内容。我不会在示例中展示的是,fetch() 还会返回一个使用 Response 对象解析的 promise。我想避开网页加载的障碍,因此选择不采取任何操作

SendBeacon()

实际上,SendBeacon() 在后台使用 Fetch API,因此它同样具有 64 KB 的载荷限制,以及它还可以确保请求在页面卸载后继续发出。其主要优势在于简单易用。它可让您通过一行代码提交数据:

window.addEventListener('unload', {
  navigator.sendBeacon('/siteAnalytics', getStatistics());
}

总结

随着各浏览器中 fetch() 的可用性不断提高,我们有望在未来某个时候将 XMLHttpRequest() 从 Web 平台中移除。浏览器供应商同意应该将其移除,但这需要一些时间。若要为所有人改善用户体验,第一步就是废弃其中一个最糟糕的用例。

照片由 Matthew Hamilton 拍摄,选自 Unsplash 网站