没有地球 太阳还是会绕。
项目里封装了一个PageWillAppearEvent与原生交互的方法,是调用IOS或安卓的方法,看了一下IOS方法ViewWillAppear是当收到视图在视窗将可见时的通知会呼叫的方法,此外项目还封装了与此类似的JS的监听事件visibilitychange,之前没怎么用过,补一下。
Page Lifecycle
Page Lifecycle 统一了网页从诞生到卸载的行为模式,并且定义了新的事件,允许开发者响应网页状态的各种转换。
一、生命周期阶段
网页的生命周期分成六个阶段,每个时刻只可能处于其中一个阶段。
-
Active:网页处于可见状态,且拥有输入焦点。
-
Passive:网页可见,但没有输入焦点。UI更新仍在执行。只可能发生在桌面同时拥有多个窗口的情况。
-
Hidden:用户桌面被其他窗口占据,页面不可见,但尚未冻结。UI更新不执行。
-
Terminated:由于用户主动关闭窗口,或在同意窗口前往其他页面,导致但钱页面开始被浏览器卸载并清出内存。这个阶段总是在Hidden阶段之后,也就是说,用户主动离开当前页面,总是先进入Hidden,再是Terminated阶段。
这个阶段会导致网页卸载,任何新任务都不会在这个阶段启动,并且如果运行时间太长,正在进行的任务可能会被终止。
-
Frozen:如果网页处于 Hidden 阶段的时间过久,用户又不关闭网页,浏览器就有可能冻结网页,使其进入 Frozen 阶段。不过,也有可能,处于可见状态的页面长时间没有操作,也会进入 Frozen 阶段。
这个阶段的特征是,网页不会再被分配 CPU 计算资源。定时器、回调函数、网络请求、DOM 操作都不会执行,不过正在运行的任务会执行完。浏览器可能会允许 Frozen 阶段的页面,周期性复苏一小段时间,短暂变回 Hidden 状态,允许一小部分任务执行。
-
Discarded:如果网页长时间处于 Frozen 阶段,用户又不唤醒页面,那么就会进入 Discarded 阶段,即浏览器自动卸载网页,清除该网页的内存占用。不过,Passive 阶段的网页如果长时间没有互动,也可能直接进入 Discarded 阶段。
这一般是在用户没有介入的情况下,由系统强制执行。任何类型的新任务或 JavaScript 代码,都不能在此阶段执行,因为这时通常处在资源限制的状况下。
网页被浏览器自动 Discarded 以后,它的 Tab 窗口还是在的。如果用户重新访问这个 Tab 页,浏览器将会重新向服务器发出请求,再一次重新加载网页,回到 Active 阶段。
二、事件
- focus 获取焦点时触发
- blur 失去焦点时触发
- visibilitychange 在网页可见状态发生变化时触发
-
freeze 在网页进入Frozen阶段时触发,通过document.onfreeze指定回调
function handleFreeze(e) { // Handle transition to FROZEN } document.addEventListener('freeze', handleFreeze); // 或者 document.onfreeze = function() { ... }
-
resume 在网页离开Frozen阶段,变为Active / Passive / Hidden阶段时触发,通过document.onresume指定回调
function handleResume(e) { // handle state transition FROZEN -> ACTIVE } document.addEventListener("resume", handleResume); // 或者 document.onresume = function() { ... }
- pageshow 用户加载页面时触发,它跟页面的可见性其实毫无关系,只跟浏览器的 History 记录的变化有关
- pagehide 用户离开当前页、进入另一个网页时触发。前提是浏览器History记录必须发生变化,跟网页可见性无关
- beforeunload 在窗口或文档即将卸载时触发。事件发生时,文档仍可见,此时卸载仍可取消。经过这个事件,网页进入 Terminated 状态。
- unload 页面正在卸载时触发。经过这个事件,网页进入 Terminated 状态。
三、获取当前阶段
如果网页处于 Active、Passive 或 Hidden 阶段,可以通过下面的代码,获得网页当前的状态。
const getState = () => {
if (document.visibilityState === 'hidden') {
return 'hidden';
}
if (document.hasFocus()) {
return 'active';
}
return 'passive';
};
如果网页处于 Frozen 和 Terminated 状态,由于定时器代码不会执行,只能通过事件监听判断状态。进入 Frozen 阶段,可以监听freeze
事件;进入 Terminated 阶段,可以监听pagehide
事件。
四、document.wasDiscarded
如果某个选项卡处于 Frozen 阶段,就随时有可能被系统丢弃,进入 Discarded 阶段。如果后来用户再次点击该选项卡,浏览器会重新加载该页面。这时,开发者可以通过判断document.wasDiscarded
属性,了解先前的网页是否被丢弃了。
if (document.wasDiscarded) {
// 该网页已经不是原来的状态了,曾经被浏览器丢弃过
// 恢复以前的状态
getPersistedState(self.discardedClientId);
}
同时,window
对象上会新增window.clientId
和window.discardedClientId
两个属性,用来恢复丢弃前的状态。
Page Visibility
一、应用场景
离开页面常用的方法是监听以下三个事件
● pagehide
● beforeload
● unload
但这三个事件在移动端有时候不会触发,因为移动端可以直接将进程切入后台,并杀掉进程。因此诞生了Page Visibility API,对于PC和移动端均适用。这个API通过监听页面的可见性,可预判页面的卸载,可以用来节省资源,例如,如果用户看不见页面,可以关闭轮询、关闭动画、关闭正在播放的音视频。
二、document.visibilityState
document.visibilityState属性返回一个字符串,表示页面当前的可见性状态,三个值:
● hidden:页面隐藏不可见
● visible:页面可见
● prerender:页面即将或正在渲染(不可见)
其中浏览器必支持hidden和visible,prerender仅由有预渲染功能的浏览器支持。只要页面可见,哪怕只露出一个角,document.visibilityState就为visible。值为hidden有以下场景:
● 浏览器最小化
● 浏览器中当前页面被切成了背景页,被其他页面覆盖
● 浏览器将要卸载(unload)页面
● 操作系统触发锁屏
三、visibilitychange事件
只要document.visibilityState发生变化,就会触发visibilitychange事件,常用的就是通过监听此事件来跟踪页面可见性变化。
● document.addEventListener()
● document.onvisibilitychange
document.addEventListener('visibilitychange', function () {
// 用户离开了当前页面
if (document.visibilityState === 'hidden') {
console.log('页面不可见');
}
// 用户打开或回到页面
if (document.visibilityState === 'visible') {
console.log('页面可见');
}
});
四、页面卸载
比较常用的是要监听页面的卸载,分为三种情况:
● 页面可见时,用户关闭 Tab 页或浏览器窗口
● 页面可见时,用户在当前窗口前往另一个页面。
● 页面不可见时,用户或系统关闭浏览器窗口。
这三种情况,都会触发visibilitychange
事件。前两种情况,该事件在用户离开页面时触发;最后一种情况,该事件在页面从可见状态变为不可见状态时触发。
visibilitychange
事件比pagehide
、beforeunload
、unload
事件更可靠,所有情况下都会触发(从visible
变为hidden
)
参考文章:
http://www.ruanyifeng.com/blog/2018/10/page_visibility_api.html
http://www.ruanyifeng.com/blog/2018/11/page_lifecycle_api.html