开发者需了解关于 Chrome 的内存模式和节能模式的须知事项

Chrome 108 引入了两种新模式(内存节省器和节能模式),让用户能够更好地控制 Chrome 如何利用系统资源。

虽然这些新模式主要面向用户,但它们确实存在一些影响,它们可能会影响网站的用户体验,因此,网站开发者需要格外注意。

这篇博文将介绍这些新模式的潜在影响,以及 Web 开发者可以采取哪些措施来确保这些模式尽可能提供最佳体验。

省内存模式

启用省内存模式后,Chrome 会主动舍弃在后台闲置了一段时间的标签页。这样可以为处于活动状态的标签页以及可能正在运行的其他应用释放内存。用户可以指示 Chrome 不要舍弃特定网站的标签页;不过,这是一种用户偏好设置,您作为开发者无法控制。

舍弃标签页后,其标题和图标仍会显示在标签页栏中,但页面本身会消失,就像该标签页已正常关闭一样。如果用户再次访问该标签页,系统会自动重新加载该页面。

对于纯内容页面,舍弃并重新加载标签页可能不会影响用户体验,但对于包含复杂用户体验流程的富媒体互动型网站,如果网站无法将页面恢复到用户离开时的位置,那么在用户体验流程的中途重新加载可能会让用户感到非常沮丧。

舍弃标签页以节省内存是 Chrome 多年来一直在采取的做法,但这只是在系统内存紧张的情况下才做到的。由于这种情况相对较少发生,Web 开发者可能并未意识到其存在。

从 Chrome 108 开始,标签页舍弃将变得更加常见,因此网站必须能够妥善处理这些情况。

处理标签页舍弃的最佳实践

标签页舍弃对 Web 开发者来说并不是一个新挑战。用户有可能在完成任务之前有意或无意地重新加载页面。因此,网站必须存储用户状态,以便在用户离开和返回时恢复用户状态。

最重要的考虑因素不是是否存储用户状态,而是何时存储。这一点非常关键,因为在舍弃某个标签页时,并没有任何事件会触发,因此开发者无法对正在发生的事件做出反应。相反,开发者需要预料到这种可能性并提前做好准备。

存储用户状态的最佳时机如下:

  • 在状态发生变化时定期发送。
  • 每当标签页进入后台时(visibilitychange 事件)。

存储状态最糟糕的时机如下:

  • beforeunload 事件回调中。
  • unload 事件回调中。

这时存储状态是最糟糕的选择,因为这些事件完全不可靠,在许多情况下都不会触发,包括在丢弃标签页时。

您可以参阅网页生命周期事件图,了解在网页被舍弃时预计会触发哪些事件。如图所示,标签页可以从“已隐藏”状态转换为“已舍弃”状态,而无需触发任何事件。

Page Lifecycle API 状态和事件流。本文档通篇介绍的状态和事件流的直观表示。

事实上,每当页面处于“隐藏”状态时,都无法保证在浏览器舍弃页面或用户终止页面之前,系统会触发任何其他事件。因此,请务必始终在 visibilitychange 事件中存储所有未保存的用户状态,因为您可能不会再有机会了。

以下代码概述了一些示例逻辑,用于在当前用户状态每次发生变化时将其加入队列以进行持久化,或者在用户将标签页置于后台或导航离开时立即进行持久化:

let state = {};
let hasUnstoredState = false;

function storeState() {
  if (hasUnstoredState) {
    // Store `state` to localStorage or IndexedDB...
  }
  hasUnstoredState = false;
}

export function updateState(newState) {
  state = newState;
  hasUnstoredState = true;
  requestIdleCallback(storeState);
}

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState === 'hidden') {
    storeState();
  }
});

检测标签页已被舍弃

如前所述,无法检测标签页即将被舍弃,但可以检测在用户返回标签页并重新加载页面后标签页是否已被舍弃。在这些情况下,document.wasDiscarded 属性将为 true。

if (document.wasDiscarded) {
  // The page was reloaded after a discard.
} else {
  // The page was not reloaded after a discard.
}

如果您想了解用户遇到此类情况的频率,可以配置分析工具来捕获此类信息。

例如,在 Google Analytics 中,您可以配置一个自定义事件参数,以确定来自标签页舍弃的网页浏览量所占的百分比:

gtag('config', 'G-XXXXXXXXXX', {
  was_discarded: document.wasDiscarded,
});

如果您是分析服务提供商,不妨考虑默认向您的产品添加此维度。

在省内存模式模式下测试您的网站

您可以通过以下方式测试网页如何处理被舍弃的情况:加载网页,然后在单独的标签页或窗口中访问 chrome://discards

chrome://discards 界面中,您可以找到要从列表中舍弃的标签页,然后点击操作列中的紧急舍弃

chrome://discards 界面的屏幕截图,显示了指向“已舍弃”标签页的链接的位置

此操作会舍弃该标签页,让您可以重新访问该标签页,并验证该网页是否已重新加载到其离开时的状态。

请注意,目前还没有办法通过 webdriver 或 puppeteer 等测试工具自动处理标签页舍弃操作;但是,由于标签页舍弃和恢复操作与页面重新加载几乎完全相同,如果您测试用户状态在用户流程期间重新加载后能否恢复,则也可能会进行舍弃/恢复操作。这两者之间的主要区别在于,在丢弃标签页时,系统不会触发 beforeunloadpagehideunload 事件,因此,只要您不依赖这些事件来保留用户状态,就可以使用重新加载来测试丢弃/恢复行为。

节能模式

启用省电模式后,Chrome 会通过降低显示屏刷新率来节省电池电量,这会影响滚动和动画保真度以及视频帧速率。

一般情况下,开发者无需执行任何操作即可支持节能模式。启用此模式后,用于动画转场效果requestAnimationFrame() 的 CSS 和 JavaScript API 会自动调整以适应显示刷新率的任何变化。

此模式可能会出现问题的主要场景是,您的网站使用基于 JavaScript 的动画,假定所有用户的刷新率都是特定值。

例如,如果您的网站使用 requestAnimationFrame() 循环,并假设回调之间会经过 16.67 毫秒的时间,那么在启用省电模式后,动画的运行速度会变慢一倍。

请注意,开发者假定所有用户的默认刷新频率为 60 Hz,这一直是个问题,因为在目前的许多设备上并非如此。

衡量显示屏刷新率

没有专用的 Web API 可以测量显示屏刷新率,一般来说,不建议尝试使用当前的 API 执行此操作。

开发者对现有 API 能做的最好的事情就是比较连续 requestAnimationFrame() 回调之间的时间戳。虽然这在大多数情况下可用于近似估算给定时间点的刷新率,但无法让您知道刷新率何时发生变化。为此,您必须持续运行 requestAnimationFrame() 意见调查,而这样就会达到为用户节省能源或电池续航时间的目标。

在节能模式下测试您的网站

如需在节能模式下测试您的网站,一种方法是在 Chrome 的设置中启用该模式,并将其配置为在设备拔下电源时运行。

如果您没有可拔出的设备,也可以按照以下步骤手动启用该模式:

  1. 启用 chrome://flags/#battery-saver-mode-available 标志。
  2. 访问 chrome://discards,然后点击切换省电模式链接(重要提示:需要启用 #battery-saver-mode-available 标志,该链接才能正常运行)。

chrome://discards 界面的屏幕截图,其中显示了用于启用节能模式的链接的位置

启用后,你可以与网站互动,验证一切是否正常:例如,动画和转场效果是否以所需的速度运行。

摘要

虽然 Chrome 的省内存模式和节能模式主要面向用户,但对开发者也有影响,因为如果处理不当,它们可能会对访问您网站的体验产生负面影响。

一般而言,这些新模式的设计遵循了现有的开发者最佳实践。如果开发者一直遵循 Web 最佳实践,他们的网站应该能够继续在这些新模式下正常运行。

不过,如果您的网站包含本博文中提到的任何做法,那么用户遇到的问题可能只有在启用这两种模式后才会加重。

与往常一样,要想确认您提供的体验是否出色,最好的方法是使用与用户相符的条件测试您的网站。