apps的生命周期对于操做系统管理系统资源来讲相当重要。在Android、IOS和Windows等的平台上,系统能够随意启动和停止apps的运行,这使得这些平台可以从新分配资源,以提供最好的操做体验给用户。javascript
但对于web应用,并无相似的生命周期机制。随着打开网页数量增长,内存、CPU、网络吞吐等系统关键资源都被过分使用,致使系统性能降低,下降了用户的操做体验。html
虽然浏览器很早以前就有提供关于生命周期的事件,好比load
、unload
和visibilitychange
等,可是这些事件只容许开发人员响应用户自行发起的生命周期状态更改。为了让网页更可靠地运行,尤为是在低功耗的设备(手机、智能手表等),浏览器须要一种主动回收和分配系统资源的机制。java
Page Lifecycle API就是为了解决这些问题而提出的方案,目标主要有三点:git
Page Lifecycle API
已经在Chrome 68上获得支持,下面将会进行详细介绍。github
Page Lifecycle API
定义了规范化的app生命周期状态,且每一个页面在一个阶段只能处于一个状态,每一个状态的改变都会有相应的事件被触发。话很少说,先上图:web
页面状态有6个,下面将分别展开描述,from表明的是前一个页面状态,to表明的是下一阶段可能会变动到的状态。windows
Active
若是页面可见并具备输入焦点,则该页面处于Active
状态api
from:passive
(focus事件)浏览器
to: passive
(blur事件)缓存
Passive
若是页面可见并不具备输入焦点,则该页面处于Passive
状态
from: avtive
(blur事件)、hidden
(visibilitychange事件)
to: avtive
(focus事件)、hidden
(visibilitychange事件)
Hidden
若是页面不可见且不处于frozen
状态,则该页面处于hidden
状态
from: passive
(visibilitychange事件)
to: passive
(visibilitychange事件)、frozen
(freeze事件)、terminated
(pagehide事件)
Frozen
若是网页处于frozen
状态下,浏览器将暂停执行页面任务队列中的可冻结任务,直到页面被解除冻结。这意味着像定时器和回调函数这样的任务不会运行。
from: hidden
(freeze事件)
to: active
(resume和pageshow事件)、passive
(resume和pageshow事件)、hidden
(resume事件)
Terminated
若是页面开始被浏览器卸载并从内存中清除,它就处于terminated
状态。在此状态下不能启动任何新任务,若是现有运行时间太长,可能会被提早终止。
from: hidden
(pagehide事件)
to: None
Discarded
当浏览器为了节省资源而卸载页面时,它处于discarded
状态。任何类型的任务、事件回调或JavaScript代码都不能在这种状态下运行,由于丢弃一般发生在资源约束下,能够理解为页面被动关闭,浏览器主动释放资源。
from: frozen
(无事件触发)
to: None
*表明的是Page Lifecycle API
新提供的事件
foucs
DOM元素获取焦点,前一个状态通常是passive
,当前状态是avtive
blur
DOM元素失去焦点,前一个状态通常是active
,当前状态是passive
visibilitychange
document
的visibilitySatate
属性发生变动,当用户导航到新页面、切换选项卡、关闭选项卡、最小化或关闭浏览器、或切换移动操做系统上的应用程序时,会触发事件。前一个状态是passive
或hidden
,,当前状态是passive
或者hidden
freeze*
页面被冻结,任务队列中的可冻结任务都中止运行。前一个状态是hidden
,当前状态是frozen
resume*
页面解除冻结状态,前一个状态是frozen
,当前状态是active
、passive
或者hidden
pageshow
当一条会话历史记录被执行的时候将会触发事件,包括了后退(前进)按钮操做,同时也会在onload
事件触发后初始化页面时触发。前一个状态可能为frozen
,当前状态为active
、passive
或hidden
pagehide
与pageshow
相似,不一样的就是导航离开当前网页时触发。先前的状态可能为hidden
,当前状态可能为frozen
或者terminated
beforeunload
窗口、文档及其资源即将被卸载。文档仍然可见,此时事件仍然能够取消。前一个状态可能为hidden
,当前状态为terminated
unload
卸载页面时触发,前一个状态可能为hidden
,当前状态为terminated
上面的图表显示了两种系统触发的页面状态:frozen
和discard
,这种页面状态和用户触发的不同,开发者没法主动感知状态变动。可是在Chrome 68上,开发者能够监听freeze
和resume
两个事件处理页面状态变动。
document.addEventListener('freeze', (event) => {
// 页面处于冻结状态
});
document.addEventListener('resume', (event) => {
// 页面解冻
});
复制代码
同时document
对象新增了wasDiscarded
属性,这个属性表明页面是否处于discarded
状态,开发者能够根据这个属性的值处理不一样的逻辑
if (document.wasDiscarded) {
// 页面被浏览器丢弃
}
复制代码
// active、passive和hidden三种状态
const getState = () => {
if (document.visibilityState === 'hidden') {
return 'hidden';
}
if (document.hasFocus()) {
return 'active';
}
return 'passive';
};
复制代码
另外,frozen
和和terminated
的状态变动能够经过监听freeze
和pagehide
事件获取。
// 保存初始页面状态
let state = getState();
// 记录状态变动并打印在控制台
// 更新当前页面状态
const logStateChange = (nextState) => {
const prevState = state;
if (nextState !== prevState) {
console.log(`State change: ${prevState} >>> ${nextState}`);
state = nextState;
}
};
// 监听生命周期事件,保持页面状态更新
['pageshow', 'focus', 'blur', 'visibilitychange', 'resume'].forEach((type) => {
window.addEventListener(type, () => logStateChange(getState()), {capture: true});
});
// 监听freeze事件
window.addEventListener('freeze', () => {
// 页面状态变动为frozen
logStateChange('frozen');
}, {capture: true});
window.addEventListener('pagehide', (event) => {
if (event.persisted) {
// event.persisted为true意味着页面是从缓存中加载的,因此是frozen状态
logStateChange('frozen');
} else {
// terminated状态
logStateChange('terminated');
}
}, {capture: true});
复制代码
Page Lifecycle
这个标准刚刚引入,并无获得所有浏览器平台的支持。有些浏览器可能在切换标签的时候不会触发blur
事件,有些浏览器没有实现freeze
和resume
事件,IE10如下版本不支持visibilitychange
事件等等...
为了让开发者更容易上手处理跨平台兼容的问题,谷歌开发了PageLifecycle.js这个库,用于观察页面生命周期状态的变化。PageLifecycle.js按事件触发顺序规范化处理了跨浏览器的差别,保证状态变动和标准规范保存一致。
对于开发者来讲,理解页面生命状态并懂得根据页面状态不懂执行不一样业务逻辑是很是重要的,下面介绍各个状态下的最佳实践:
Active
active
状态是用户最关键的时间,所以也是页面响应用户输入最重要的时间,任何可能阻塞主线程执行的非UI渲染任务均可以放到requestidlecallback
或者web worker
执行
Passive
在此状态下,用户没有和页面进行交互,但页面仍然处于可视状态,因此UI和动画须要保持渲染状态,从active
状态过渡到passive
是一个保存页面状态的好时机,好比一些表单值。
Hidden
页面被隐藏或者关闭,中止全部与用户交互、UI渲染有关的任务,并及时保存应用状态
Frozen
在frozen
状态下,可冻结任务将会被挂起直到页面解冻(有可能永远不会发生)。在这个状态下,开发者要作如下几点:
Terminated
一般在此状态下不须要处理任何任务,由于这个阶段的任务不能保存可靠执行,有可能被强行终止。但你也能够作一些状态持久化或者埋点分析的任务