美文网首页程序员
前进之前与后退之后-pageshow&pagehide

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

作者: xurna | 来源:发表于2020-08-05 14:16 被阅读0次

    需求背景

    一个页面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解决方案

    相关文章

      网友评论

        本文标题:前进之前与后退之后-pageshow&pagehide

        本文链接:https://www.haomeiwen.com/subject/nkkkrktx.html