vue the vue cycle function triggered by the timer cannot be cleared by clearTimeout. The event code is executed in sequence

Recently, I worked on a mobile project and encountered a demand: if there is no operation on the home page for 20 seconds, I will automatically exit and log in. Other pages have no operation for 20 seconds and automatically jump to the home page.

The so-called no operation includes user behaviors such as click, touch and slide.
This requirement is actually very simple. The idea is to use the timer setTimeout to set the time and monitor whether there are click, touch, sliding and other events on the page. If not, jump to the home page or log out as soon as the time comes. If there is an event, clear the timer and restart the timer

Let's implement this logic first and try putting it on the home page first

<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">Tap the screen to continue</strong>
    </div>
  </div>
</template>
<script>
export default {
  created() {
    this.screenClick()
  },
  methods:{
    /**
     * The screen is clicking
     */
    screenClick(){
      // console.log('screen click ')
      this.delayUserHandle()
    },
    /**
     * delay
     */
    delayUserHandle(){
      window.clearTimeout(this.timeOut)
      // console.log('Start delay ')
      // console.log(this.timeOut)
      this.timeOut = window.setTimeout(()=> {
        // console.log('delay end ')
        const {name} = this.$route
         if(name==='Home'){
            // home page
            this.$store.dispatch('logout')
          }else{
            this.$store.dispatch('logout')
            this.$router.replace('/')
            }
      },20000)
    },
    /**
     * Touch start
     */
    touchStart(){
      window.clearTimeout(this.timeOut)
    },
    /**
     * Touch slide
     */
    touchmove(){
      window.clearTimeout(this.timeOut)
    },
    /**
     * Touch end
     */
    touchEnd(e){
      // console.log('touchEnd')
      // console.log(e)
      this.delayUserHandle()
    }
  },
}
</script>

ok, this has realized the 20 second no operation exit of a page.
But what we need is any page, not one, so there are many common places in this area. So we can extract the public part and use mixins here.
Create a new clear login info JS file, which is placed in the mixins folder under src. At the same time, we have to destroy the timer after the page leaves and is destroyed. The places where timers can be destroyed are beforeDestroy,destroyed, beforeRouteLeave. At the beginning, I didn't consider so much. I directly used beforeRouteLeave, which is the routing guard before the page leaves.
clear-login-info.js code is as follows:

//Clear user information
import {mapState} from "vuex";

export default {
  data(){
    return{
      timeOut:null,//timer
    }
  },
  computed:{
    ...mapState(['hasLogin'])
  },
  created() {
    this.timeOut = setTimeout(()=>{
      this.$store.dispatch('clearInfo')
    },30000)
    this.screenClick()
  },
  methods:{
    /**
     * The screen is clicking
     */
    screenClick(){
      this.delayUserHandle()
    },
    /**
     * delay
     */
    delayUserHandle(){
      window.clearTimeout(this.timeOut)
      // console.log('Start delay ')
      // console.log(this.timeOut)
      this.timeOut = window.setTimeout(()=> {
        // console.log('delay end ')
        const {name} = this.$route
      if(name==='Home'){
            // home page
            this.$store.dispatch('logout')
          }else{
           this.$store.dispatch('logout')
            this.$router.replace('/')
            }
      },20000)
    },
    /**
     * Touch start
     */
    touchStart(){
      clearTimeout(this.timeOut)
    },
    /**
     * Touch slide
     */
    touchmove(){
      clearTimeout(this.timeOut)
    },
    /**
     * Touch end
     */
    touchEnd(){
      this.delayUserHandle()
    }
  },
  beforeRouteLeave(to,from,next){
    clearTimeout(this.timeOut)
    next()
  }
}

Is that it? Indeed, it has achieved the effect, but only in this way, there is no article. There is a Bug
After use, it is found that after jumping from the home page to other pages, no matter whether there is operation or not, it will jump to the home page after 20 seconds. And pages that do not use this effect will also jump to the home page.
One thing to note here is that the timer does not automatically eliminate after the page is destroyed. The timer needs to be cleared manually. But it's already written in beforeRouteLeave. Then there is only one reason. The timer that really works has not been cleared!

As mentioned earlier, there are three places where the timer can be cleared before the page leaves, beforeDestroy,destroyed and beforeRouteLeave. If the timer fails to be eliminated before jumping to the next page, we will try to trigger it at the last time. First of all, destroyed must be triggered after beforeDestroy, so we need to compare who triggered destroyed and beforeroutleave last. The verification method is also very simple, direct to console Log can be printed.
First, on app Vue gets a route jump. Here, it is assumed that there are two page home pages and about

<template>
<div>
   <router-link to="/mkf">about</router-link>
    <router-link to="/home">home page</router-link>
    <router-view></router-view>
</div>
</template>

Then we check the print on the home page

<!--Home-->
<template>
  <div>Home</div>
</template>
<script>
export default {
  destroyed() {
    console.log('Destroy page')
  },
  beforeRouteLeave(to,from,next){
    console.log('Route guard beforeRouteLeave')
    next()
  }
}
</script>

Run the project, click on the home page to jump to the "about" page and see the browser print results

The result is destroyed and finally executed. OK, let's put the clear timer in destroyed and clear login info JS is replaced by destroyed

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

Continuing the test, we found that the timer was still not cleared.
Because when destroyed is executed, the page has actually switched to a new page. At this time, the timer on the page may no longer exist, so it is not cleared successfully.

This problem is annoying, so I won't continue the test. In short, the timer cannot be cleared in destroyd. This scheme cannot work. In this way, it will be found that the timer is often not cleared.

After thinking about it, this demand can be solved by using timer and vuex.

Keywords: Javascript Front-end Vue Vue.js

Added by R4nk3d on Tue, 01 Feb 2022 07:08:10 +0200