程序员

前进之前与后退之后-pageshow&pagehide

2020-08-05  本文已影响0人  xurna

需求背景

一个页面A需要记录在页面点击某个链接B时的时间time1,与后退返回该页面时的时间time2,得到中间的时间差time2-time1
此处的难点在于:
1、点击跳出A的B是不确定的,有可能点击的是广告(点击的是广告,则几乎很难监听到点击广告的事件),有可能是一个页面链接(页面链接有可能非常多,不可能每个链接都加一个监听事件);
2、记录离开页面前的时间,与返回页面的时间,离开页面有可能是在当前页面刷新打开页面,有可能是新开窗口打开,情况有多种。

解决思路

巧用pagehidepageshowdocument.hiddenvisibilitychange组合实现。

pageshowpagehide 事件

手机上的浏览器有一个特性,名叫“往返缓存”(back-forward cache,或bfcache),可以在用户使用浏览器的“后退”和“前进”按钮时加快页面的转换速度。这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。如果页面位于bfcache中,那么再次打开该页面就不会触发load事件。尽管由于内存中保存了整个页面的状态,不触发load事件也不应该会导致什么问题,但为了更形象地说明bfcache的行为,Firefox还是提供了一些新事件。 第一个事件就是pageshow,这个事件在页面显示时触发,无论页面是否来自bfcache。在重新加载页面中,pageshow会在load事件触发后触发;而对于bfcache中的页面,pageshow会在页面状态完全恢复的那一刻触发。
1)load 和 unload 事件监听web页面的进入和离开,一般用于页面的首次加载、刷新和关闭等操作的监听;
2)pageshowpagehide 事件多用于监听浏览器的前进和后退等。

1、pageshow和load区别:
pageshow 事件类似于 load 事件,load 事件在页面第一次加载时触发, pageshow 事件在每次加载页面时触发,即 load 事件在页面从浏览器缓存中读取时不触发。

一般情况下,移动端浏览器会将当前已访问页面存入缓存中,缓存中保存着页面数据,DOM和js的状态,前进和后退操作时直接从浏览器缓存中读取页面内容,而不进行页面刷新,所以监听前进和后退操作时可用pageshow事件。

触发时间:load先触发,pageshow后触发。

2、查看是否读取缓存:
为了查看页面是直接从服务器上载入还是从缓存中读取,你可以使用 Event 对象的 persisted 属性来判断。 如果页面从浏览器的缓存中读取该属性返回 ture,否则返回 false

3、示例:

window.addEventListener('pageshow', function(event) {
    console.log(event.persisted);
})

4、pagehide和unload事件的区别:
pagehide 事件类似于 unload 事件,在用户离开网页时触发(如点击一个链接、刷新页面、提交表单、关闭浏览器、前进、后退等)。
页面缓存:pagehide触发可以缓存页面,但unload 事件触发后无法缓存。
触发时间:pagehide先触发,unload后触发。

2、查看是否读取缓存:
同pageshow

pageshow,pagehide兼容情况:基本移动端上用都兼容


image.png

问题:但是有时候在一些机型中event. persisted判断并不准确,所以可以引用window.performance.navigation.type做兼容处理。

window.performance对象

performance.navigation.type是一个无符号短整型,接口呈现了如何导航到当前文档的信息。它有四种type类型:
1、TYPE_NAVIGATE (0):当前页面是通过点击链接,书签和表单提交,或者脚本操作,或者在url中直接输入地址,type值为0。
2、TYPE_RELOAD (1):点击刷新页面按钮或者通过Location.reload()方法显示的页面,type值为1:。
3、TYPE_BACK_FORWARD (2):页面通过历史记录和前进后退访问时。type值为2。
4、TYPE_RESERVED (255): 任何其他方式,type值为255。
所以type为2可以作为页面后退或者前进时的一个判断依据。

window.addEventListener('pageshow', (e) => {
  if (e.persisted || (window.performance && window.performance.navigation.type === 2)) {
    // 页面后退或者前进时操作
  }

document.hidden属性

页面可见性判断:document.hiddenvisibilitychange事件,当前页面不在视野范围内,则会触发visibilitychange事件,并且改变document.hidden的值为true,当回到页面视野当中,也会触发事件改变document.hidden的值为false。

    // 兼容浏览器
    const hidden = 'hidden' in document ? 'hidden' : 'webkitHidden' in document ? 'webkitHidden' : 'mozHidden' in document ? 'mozHidden' : null;
    const event = hidden.replace(/hidden/i, 'visibilitychange');
    document.addEventListener(event, () => {
      console.log('当前页面是否被隐藏:', document[hidden]);
    });

具体实现

页面A点击页面中的链接B跳去新链接,记录离开A页面的时间与返回A页面的时间。
B页面离开方式:
1、当前窗口打开页面(非跳出型):监听pagehide & document.hidden=true
2、新开窗口打开页面(跳出型):监听pagehide & document.hidden=true

返回页面A方式:
1、返回后页面被动刷新:监听pageshow
2、返回后页面不刷新:监听document.hidden=false

终上所述,为了兼容设备前进后退的多种差异情况,需要融合几种监听方式,以达到覆盖多种情况的效果(经测试,在安卓版本5,7,10上组合方式都通过测试,ios也通过了测试)。

// 兼容浏览器
const hidden = 'hidden' in document ? 'hidden' : 'webkitHidden' in document ?
 'webkitHidden' : 'mozHidden' in document ? 'mozHidden' : null;
const event = hidden.replace(/hidden/i, 'visibilitychange');
document.addEventListener(event, () => {
  if(document[hidden]) {
    // 隐藏:记录离开时间
  } else {
    // 显示:记录返回时间
  }
});

window.addEventListener('pageshow', (e) => {
  if (e.persisted || (window.performance && window.performance.navigation.type === 2)) {
    // 页面后退时操作:记录返回时间
  }
}

window.addEventListener('pagehide', (e) => {
   // 页面离开时操作:记录离开时间
}

后记

在vue中,如果已经创建了Vue示例再实行监听pageshow时间的话,是会失效的,所以,需要在未创建Vue示例前监听pageshow事件,并且可以通过window.postMessage延迟传递信息:

window.addEventListener('pageshow', (t) => {
  // persisted:查看页面是直接从服务器上载入还是从浏览器缓存中读取,true:缓存读取
  if (t.persisted || (window.performance && window.performance.navigation.type === 2)) {
    setTimeout(() => {
      window.postMessage('pageshowEvent', window.location.origin);
    }, 2000);
  }
  new Vue({
      el: '#app',
      template: '<Main />',
      components: { Main },
    });
});

// 实现vue文件中
window.addEventListener('message', (e) => {
   if (e.origin !== window.location.origin && e.data !== 'pageshowEvent') return;
   // 后续处理
}, false);

参考文章

  1. pageshow和pagehide应用场景
  2. 移动端返回强制刷新页面pageshow事件persisted总为false解决方案
上一篇下一篇

猜你喜欢

热点阅读