使用Page Visibility API来提高页面性能和用户体验

2,067 阅读4分钟

这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战

前言

Chrome中,每一个网页标签都是一个进程,当该标签处于不可见状态一定时间后会处于休眠状态以节省内存等系统资源。我们在开发时候有时需要检测用户是否处于当前页面,以满足功能上的需求。

在以往,判断用户是否处于当前页面,通常是判断pagehidebeforeunloadunload这三个事件,但是在移动端上不一定全部都会触发,因为当前页面被系统切换到另外一个应用,以及系统杀死浏览器进程,是无法监听到的。当用户切换应用,然后由于内存不足或太久未使用,系统会直接杀死浏览器进程,此时unloadbeforeunload会来不及触发。

Page Visibility API,不管移动端还是桌面,所有情况下,都能监听到页面的可见性发生变化。使用它可以增强用户体验,当用户不在当前页面时,可以停止音视频播放,网络请求,动画加载等,以节约资源和电量。

接口特性

Page Visibility API的方法和属性被挂载到document对象上,我们随时都能调用它,它具有以下属性:

  • document.hidden:窗口是否不可见,只读属性,当窗口处于完全不可见时为true
  • document.visibilityState:只读属性,表示当前窗口的状态: 'hidden' | 'visible' | 'prerender',只要当前页面有一丁点可见都为true
  • visibilitychange:事件类型,当窗口的可见性发生变化时触发。

推荐使用document.visibilityState而不是document.hidden,它除了能检测页面的可见性还能检测页面是否处于预渲染阶段。

document.hidden

只读属性,返回一个布尔值,表示页面是否可见,为true时表示完全不可见。

document.visibilityState

相对于document.hiddendocument.visibilityState能检测更复杂的场景。它有三个状态值:

  • hidden:页面完全不可见。
  • visible:页面至少一部分可见。
  • prerender:页面即将或正在渲染,处于不可见状态。

prerender状态只在支持"预渲染"的浏览器上才会出现,比如 Chrome 就有预渲染功能,可以在用户不可见的状态下,预先把页面渲染出来,等到用户要浏览的时候,直接展示渲染好的网页。

以下情况为hidden:

  • 浏览器最小化。
  • 浏览器没有最小化,但是当前页面切换为其他页面。
  • 浏览器将要卸载(unload)页面。
  • 系统锁屏。

上面四种场景涵盖了页面可能被卸载的所有情况。除了hiddenprerender,其他时候只要有一点页面显示都为visible

页面卸载之前,document.visibilityState一定会变成hidden状态,这也是W3C设计这个API的主要目的。

visibilitychange

只要document.visibilityState属性发生变化,就会触发visibilitychange事件。因此,可以通过监听这个事件来检测页面可见性的变化。

document.addEventListener("visibilitychange",()=>{
   if(document.visibilityState==="hidden"){
       console.log("页面不可见")
   }
   else{
       console.log("页面可见")
   }
})

document.visibilityState属性只针对顶层窗口,内嵌的<iframe>页面的document.visibilityState属性由顶层窗口决定。使用 CSS 属性隐藏<iframe>页面(比如display: none;),并不会影响内嵌页面的可见性。

MDN的描述:

<iframe>的可见性状态与父文档相同。使用CSS属性(例如display: none;)隐藏<iframe>不会触发可见性事件或更改框架中包含的文档的状态。

使用场景

节省音频流量

当用户离开当前页面时,我们可以暂停音视频的播放,以节省用户的流量。

<video id="video">
  <source id='mp4' src="movie.mp4" type='video/mp4'/>
</video>
const onVisibilityChange = () => {
  const video = document.getElementById("video");
  // 页面完全不可见时,暂停播放,可见时则恢复播放
  return document.hidden
    ? video.pause()
    : video.play();
}

document.addEventListener("visibilitychange", onVisibilityChange);

一个栗子: Demo: Page Visibility API (daniemon.com)

资源懒加载

除了节省流量,我们也可以结合webpack的动态加载模块import()实现资源的懒加载。例如,当用户离开当前页面,我们可以请求用户接下来可能用到的资源并缓存起来,以提高使用体验。

let loaded = false;
const onVisibilityChange = () => {
  if (!loaded && document.visibilityState === 'visible') {
    Promise.all([
      import(/* webpackChunkName: "bundle-[index]"*/ './dist'),
      import(/* webpackPrefetch: 0 */ './images')
    ]).then(() => {
      loaded = true;
    });
  }
};

document.addEventListener('visibilitychange', onVisibilityChange);

其他

除了上面提到的场景外,我们还能用来关闭定时器,挂起webSocket轮询,停止页面动画特效等等,这些都能进一步提高页面的性能和用户体验。

总结

image.png

目前该接口得到广泛的支持,web性能工作组织Web Performance Working Group)也提倡我们在构建应用时使用该API来提升提升应用性能和用户体验。

参考

往期文章