vue 由 clearTimeout无法清除定时器引发的vue 周期函数,事件代码执行顺序思考
vue 由 clearTimeout无法清除定时器引发的vue周期函数,事件代码执行顺序思考最近做个移动的项目,遇到需求:首页无操作20秒,自动退出登录。其他页面20秒无操作,自动跳转首页。所谓的无操作,包括点击,触摸,滑动等用户行为。这需求其实也很简单,思路就是使用定时器setTimeout设定时间,监听页面是否有点击,触摸,滑动等事件在操作,如果没有,则时间一到,就跳转首页或者退出登录,如果有
vue 由 clearTimeout无法清除定时器引发的vue 周期函数,事件代码执行顺序思考
最近做个移动的项目,遇到需求:首页无操作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即可解决。
更多推荐
所有评论(0)