美文网首页
vue 由 clearTimeout无法清除定时器引发的vue

vue 由 clearTimeout无法清除定时器引发的vue

作者: 东扯葫芦西扯瓜 | 来源:发表于2022-01-31 09:52 被阅读0次

    最近做个移动的项目,遇到需求:首页无操作20秒,自动退出登录。其他页面20秒无操作,自动跳转首页。

    所谓的无操作,包括点击,触摸,滑动等用户行为。
    这需求其实也很简单,思路就是使用定时器setTimeout设定时间,监听页面是否有点击,触摸,滑动等事件在操作,如果没有,则时间一到,就跳转首页或者退出登录,如果有事件发生,则清除定时器,然后重启定时器

    那我们先来实现下这个逻辑,先放首页试试

    <template>
      <div class="home" @click="screenClick" @touchmove="touchmove" @touchstart="touchStart" @touchend="touchEnd">
        <div class="home-img-box"></div>
        <div class="login-box row-center">
          <strong class="login-text">轻触屏幕,继续操作</strong>
        </div>
      </div>
    </template>
    <script>
    export default {
      created() {
        this.screenClick()
      },
      methods:{
        /**
         * 屏幕在点击
         */
        screenClick(){
          // console.log('屏幕点击')
          this.delayUserHandle()
        },
        /**
         * 延迟
         */
        delayUserHandle(){
          window.clearTimeout(this.timeOut)
          // console.log('开始延迟')
          // console.log(this.timeOut)
          this.timeOut = window.setTimeout(()=> {
            // console.log('延迟结束')
            const {name} = this.$route
             if(name==='Home'){
                // 首页,
                this.$store.dispatch('logout')
              }else{
                this.$store.dispatch('logout')
                this.$router.replace('/')
                }
          },20000)
        },
        /**
         * 触摸开始
         */
        touchStart(){
          window.clearTimeout(this.timeOut)
        },
        /**
         * 触摸滑动
         */
        touchmove(){
          window.clearTimeout(this.timeOut)
        },
        /**
         * 触摸结束
         */
        touchEnd(e){
          // console.log('touchEnd')
          // console.log(e)
          this.delayUserHandle()
        }
      },
    }
    </script>
    

    ok,这个就已经实现了一个页面的20秒无操作退出。
    但是我们需要的是任意页面,而不是一个,所以,这一块有很多公用的地方。所以我们可以将公共的部分抽出来,这里使用mixins 混入。
    新建 clear-login-info.js文件,这里放在src下的mixins文件夹中,同时,我们得在页面离开,销毁后,将定时器销毁。可以销毁定时器的地方有beforeDestroy,destroyed,beforeRouteLeave。开始的时候,没考虑那么多,直接用beforeRouteLeave,就是页面离开前的路由守卫。
    clear-login-info.js 代码如下:

    //清除用户信息
    import {mapState} from "vuex";
    
    export default {
      data(){
        return{
          timeOut:null,//定时器
        }
      },
      computed:{
        ...mapState(['hasLogin'])
      },
      created() {
        this.timeOut = setTimeout(()=>{
          this.$store.dispatch('clearInfo')
        },30000)
        this.screenClick()
      },
      methods:{
        /**
         * 屏幕在点击
         */
        screenClick(){
          this.delayUserHandle()
        },
        /**
         * 延迟
         */
        delayUserHandle(){
          window.clearTimeout(this.timeOut)
          // console.log('开始延迟')
          // console.log(this.timeOut)
          this.timeOut = window.setTimeout(()=> {
            // console.log('延迟结束')
            const {name} = this.$route
          if(name==='Home'){
                // 首页,
                this.$store.dispatch('logout')
              }else{
               this.$store.dispatch('logout')
                this.$router.replace('/')
                }
          },20000)
        },
        /**
         * 触摸开始
         */
        touchStart(){
          clearTimeout(this.timeOut)
        },
        /**
         * 触摸滑动
         */
        touchmove(){
          clearTimeout(this.timeOut)
        },
        /**
         * 触摸结束
         */
        touchEnd(){
          this.delayUserHandle()
        }
      },
      beforeRouteLeave(to,from,next){
        clearTimeout(this.timeOut)
        next()
      }
    }
    
    

    这就完了吗?的确是达到效果了,但是,仅仅如此也就没有本文了。有Bug.
    使用后发现,从首页跳到其他页面后,不管是否有操作,20秒后都会跳转到首页。而且没有使用该效果的页面也会跳转到首页。
    这里有个需要注意的地方就是,定时器并不会在页面销毁后自动消除。需要手动清除定时器。但是这里已经在beforeRouteLeave里面写了。那么原因就只要一个,真正起作用的定时器并没有被清除!

    前面说过页面离开前可以清除定时器的有三个地方,beforeDestroy,destroyed和beforeRouteLeave,那么如果是跳转下一页面前,定时器没有消除成功,那我们就尽量在最后的时机来触发试试。首先destroyed肯定在beforeDestroy之后触发,所以我们需要比较下destroyed和beforeRouteLeave到底谁最后触发。验证方式也很简单,直接console.log打印即可。
    首先在app.vue弄个路由跳转,这里假设有两个页面 首页和关于

    <template>
    <div>
       <router-link to="/mkf">关于</router-link>
        <router-link to="/home">首页</router-link>
        <router-view></router-view>
    </div>
    </template>
    

    然后我们在首页来检测打印

    <!--Home-->
    <template>
      <div>Home</div>
    </template>
    <script>
    export default {
      destroyed() {
        console.log('页面销毁')
      },
      beforeRouteLeave(to,from,next){
        console.log('路由守卫beforeRouteLeave')
        next()
      }
    }
    </script>
    
    

    运行项目,在首页点击 跳转到 "关于"页面,看看浏览器打印结果


    在这里插入图片描述

    结果是destroyed最后执行。好,那我们吧清除定时器放在destroyed里面,将clear-login-info.js的beforeRouteLeave替换为destroyed

      destroyed() {
        // console.log('销毁')
        window.clearTimeout(this.timeOut)
      },
    

    继续测试,我们发现,定时器依然没有被清除。
    因为destroyed执行的时候,页面其实已经切换到新页面了。这时候去清楚上页面的定时器,变量可能已经不存在,所以没有清除成功。

    这个问题比较烦,就不再继续测试了。总之不能在destroyd清除定时器,这方案不得行,这样使用会发现,定时器经常没有被清除掉。

    思考了一下,这个需求,使用定时器加vuex即可解决。

    相关文章

      网友评论

          本文标题:vue 由 clearTimeout无法清除定时器引发的vue

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