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

Chrome 108 引入了两种新模式:省内存模式和节能模式,让用户能够更好地控制 Chrome 使用系统资源的方式。

虽然这些新模式主要面向用户,但是它们对网站开发者来说确实有一些重要的意义,因为它们可能会影响您网站的用户体验。

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

省内存模式

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

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

对于纯内容网页,舍弃并重新加载标签页可能不会影响用户体验,但对于用户流的丰富且互动性较强的网站,如果网站无法将网页恢复到用户上次离开的位置,那么在流程中间进行重新加载可能会非常令人沮丧。

舍弃标签页以节省内存是 Chrome 多年来一直采用的做法,但只能在系统面临内存压力的情况下这样做。由于这种现象相对罕见,网络开发者可能没有意识到这一点。

从 Chrome 108 开始,舍弃标签页将变得越来越常见,因此网站必须妥善处理这类情况,这一点至关重要。

处理标签页舍弃的最佳做法

标签页舍弃并非 Web 开发者面临的一项新挑战。用户在完成任务之前随时可能(无论是有意还是无意)重新加载页面。因此,网站必须存储用户状态,以便在用户离开和返回时恢复用户状态。

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

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

  • 当状态发生变化时定期检查。
  • 每当标签页在后台运行时(visibilitychange 事件)。

存储状态的最坏时间如下:

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

这些是最糟糕的状态存储时间,因为这些事件完全不可靠,并且在很多情况下(包括标签页被舍弃时)也不会触发。

您可以参考页面生命周期事件示意图,了解在网页被舍弃时预期会触发哪些事件。从该图中可以看出,标签页可以从“隐藏”状态变为“已舍弃”状态,而不触发任何事件。

页面生命周期 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 API 和 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 的省内存模式和节能模式主要是面向用户的功能,但它们确实会对开发者造成影响,因为如果处理不当,可能会对您网站的访问体验产生负面影响。

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

但是,如果您的网站包含本博文中提到的任何做法,您的用户可能会遇到的问题可能会在启用这两种模式后增加。

与往常一样,确认您的网站是否提供良好体验的最佳方法是,在符合用户条件的情况下测试您的网站。