【Web技术】850- 深入了解页面生命周期API
译者:@飘飘
作者:@Viduni Wickramarachchi
原文:https://blog.bitsrc.io/page-lifecycle-api-a-browser-api-every-frontend-developer-should-know-b1c74948bd74
前言
曾经碰到过这么个场景,统计用户在该游戏停留时间。文章由@飘飘翻译。
每个前端开发人员都应该知道的浏览器API
作为用户,我们在浏览网页时总喜欢一心多用。因此,打开多个浏览器标签页是很常见的,因为这有助于并行完成事情。但同时,每一个标签页都会消耗系统资源,比如内存和CPU。
由于不可能限制用户打开新的浏览器标签页并将其留下,因此浏览器采取了一些措施,以在浏览器标签页不活动时重新分配资源。
现在的现代浏览器有时会在系统资源紧张的情况下暂停页面或完全丢弃页面--菲利普-沃尔顿。
那么你可能会有疑问,既然浏览器已经处理好了,我们为什么还要担心这个问题呢?
并非完全如此,浏览器会照顾到一切。此外,这些浏览器的干预会直接影响到JavaScript的执行。好消息是,几乎所有的现代浏览器都通过页面生命周期API将这些干预作为事件暴露出来。
页面生命周期API
顾名思义,页面生命周期API将网页生命周期的钩子暴露给JavaScript。然而,这并不是一个全新的概念。页面可见性API存在了有一段时间,向JavaScript揭示了一些页面可见性事件。
然而,如果你碰巧在这两者之间做出选择,值得一提的是Page Visibility API的一些限制。
它只提供网页的可见和隐藏状态。
它不能捕获被操作系统丢弃的页面(Android、IOS和最新的Windows系统可以终止后台进程以保存系统资源)。
我们来看看页面生命周期API所暴露的页面生命周期状态。
页面生命周期API状态
在API中介绍了6种状态,其中有两种状态与我们颇为相关。其中,有两个状态与我们相当相关。
FROZEN--CPU暂停的生命周期状态(隐藏的网页会被冻结以节约资源)。
如果一个网页被隐藏了很久,而用户没有关闭网页,浏览器会将其冻结,并将网页移动到这个状态。但是,正在运行的任务会继续进行,直到完成。但定时器、回调函数执行和DOM操作将被停止以释放CPU。
当我查看电脑上Chrome浏览器的资源消耗时,我观察到两个活动标签页分别消耗了14.7%和11%的CPU,而冻结的标签页消耗了近0%的CPU。
DISCARDED - 为了节省资源,将冻结状态移动到Discarded状态。
假设一个网页长时间处于冻结状态,在这种情况下,浏览器会自动将网页卸载到丢弃状态,以节省资源。在这种情况下,浏览器会自动将页面卸载到丢弃状态,释放一些内存。而如果用户再次访问被丢弃的页面,浏览器会重新加载页面,回到活动状态。
值得注意的是,用户一般会在资源受限的设备中体验到丢弃状态。
除了以上两种状态外,API中还引入了其他四种状态,分别是:。
ACTIVE - 页面可见并有输入焦点。
PASSIVE - 页面可见,但没有输入焦点。
HIDDEN - 页面不可见(也没有冻结)。
TERMINATED - 页面被卸载并从内存中清除。
你可以通过看下图找到生命周期状态和过渡的细节。
如何应对生命周期状态?
现在我们已经了解了页面生命周期API,让我们看看如何响应每个事件。
这里最重要的是确定当应用程序达到每个状态时,哪些需要保留,哪些需要停止。
ACTIVE状态--由于用户在页面上是完全活跃的,所以你的网页应该完全响应用户的输入。任何UI阻塞任务都应该被去掉优先级,比如同步和阻塞网络请求。
PASSIVE状态--即使用户在这个阶段没有与页面进行交互,他们仍然可以看到它。因此你的网页应该流畅地运行所有的UI更新和动画。
HIDDEN状态 - 隐藏状态应该被视为用户在网页上的会话的结束。你可以在此时坚持未保存的应用状态,并停止任何用户不需要在后台运行的UI更新或任务。
Frozen状态 - 任何可能影响其他标签页的定时器和连接都应该在这个阶段终止。例如,你应该关闭所有打开的IndexedDB连接,任何打开的Web Socket连接,释放任何被持有的Web锁,等等。
Terminated状态 - 由于会话结束逻辑是在隐藏状态下处理的,所以一般不需要任何操作。
Discarded状态 - 这个状态是应用程序无法观察到的。因此,任何可能的丢弃的准备工作都应该在隐藏或冻结状态下进行。然而,你可以在页面加载时通过检查document.wasDiscarded来对页面的任何恢复做出反应。
好了,现在我们知道在每个状态下要做什么了,让我们看看如何在我们的应用程序中捕获每个状态。
如何在代码中捕获生命周期状态?
你可以使用下面的JavaScript函数来确定一个给定页面的主动、被动和隐藏状态。
const getState = () => {
if (document.visibilityState === 'hidden') {
return 'hidden';
}
if (document.hasFocus()) {
return 'active';
}
return 'passive';
};
随着Chrome 68的发布,开发者可以通过监听文档对象上的冻结和恢复事件来观察隐藏标签何时被冻结和解冻。
document.addEventListener('freeze', (event) => {
// The page is now frozen.
});
document.addEventListener('resume', (event) => {
// The page has been unfrozen.
});
要确定一个页面在隐藏标签页中是否被丢弃,可以使用以下代码。
if (document.wasDiscarded) {
// Page was previously discarded by the browser while in a hidden tab.
}
上面提到的wasDiscarded属性可以在页面加载时观察。
浏览器兼容性
一些旧的浏览器不具备检测其网页何时被冻结或丢弃的能力。不过,随着Chrome 68的发布,也加入了预测网页下一步状态的能力。
已知的兼容性问题
一些浏览器在切换标签页时没有触发模糊事件,这样可以避免页面进入被动状态。
老版本的IE(10及以下)没有实现visibilityChange事件。
Safari在关闭标签页时没有可靠地触发pagehide或visibilitychange事件。
为了克服跨浏览器的不兼容性,Google开发了一个名为Pagelifecycle.js的库,作为以下浏览器的多维填充。
总结
当用户没有积极参与时,网页不应该消耗过多的资源。此外,你的应用程序还应该知道系统执行的管理任务。Page Lifecycle API介绍了一种简单的方法来让你的应用程序知道这些事件。
虽然它更多地与高级用例相关,但我们可以通过了解它的功能来开发高效的网络应用。因此,我们可以为终端用户提供更好的体验。
1. JavaScript 重温系列(22篇全) 2. ECMAScript 重温系列(10篇全) 3. JavaScript设计模式 重温系列(9篇全) 4. 正则 / 框架 / 算法等 重温系列(16篇全) 5. Webpack4 入门(上)|| Webpack4 入门(下) 6. MobX 入门(上) || MobX 入门(下) 7. 100+篇原创系列汇总 回复“加群”与大佬们一起交流学习~
点击“阅读原文”查看 100+ 篇原创文章