1、setInterval路由跳转继续运行并没有销毁
如果你在一个组件中使用setInterval定时器,路由跳转时未清除定时器,它不会销毁,还回在后台不断的运行,如果运算量大的话会造成卡顿。
解决方法:
在组件生命周期beforeDestroy中停止setInterval,正好也说下为什么好多会把定时器变量定义在data中,很多时候我们可能用到定时器这个变量标识,在data中定义用起来更加方便。。。
export default {
name: "first",
data(){
return{
timer:null
}
},
mounted(){
this.timer = setInterval(()=>{
console.log("1")
},1000)
},
beforeDestroy(){
clearInterval(this.timer)
}
};
2、滚动行为
在vue文档中有详细的说明,我在重复一遍:
如果不加 scrollBehavior 方法 ,切换导航时,滚动条会记录原先的位置,不会发生改变!!!通常在开发时,我们都希望从一个页面切换到另一个页面时,页面都是置顶的,为解决这个问题你可以在router实例中配置 scrollBehavior 方法:
const router = new VueRouter({
routes: [...],
scrollBehavior (to, from, savedPosition) {
if(savedPosition){
return savedPosition
}else{
return {x:0,y:0}
}
}
})
to : 新路由地址 对象
from : 原先路由地址 对象
savedPosition : 滚动条的位置,此参数,只有 通过浏览器的 前进 或 后退 按钮触发时才能触发可用。。。
{ x:0,y:0 } : 路由切换时, 让页面滚动到顶部;也就是路由切换时定义滚动条的x轴和y轴的位置。
3、拦截路由跳转,询问是否要切换导航
为防止用户突然切换导航,而未保存已输入的信息。
使用beforeRouteLeave 守卫
beforeRouteLeave(to,from,next){
if(用户已输入的信息){
alert("您确定要退出吗")
}
next()
},
实际应用:
多个路由同时复用一个组件时,比如登录和注册页,可复用组件用props接收来自父组件传来的不同内容,渲染不同页面,这里边,登录页和注册页的 账户 和 密码框 都是一样的,如果用户在登录页输入完信息,直接切换到注册页,那么注册页会显示用户在登录页输入的 账号和密码,这样是肯定不行的!!!
所以我们可以使用beforeRouteLeave 守卫来判断,当用户在登录页输入完信息 且 用户直接切换到了注册页,那么我们直接清空输入框的内容,或者不让跳转也行,看自己的需求。
下边我是直接清空输入框跳转到注册页 或登录页:
export default {
name: 'Login',
beforeRouteLeave(to,from,next){
// 切换到注册页面,如果登录页面
if(to.fullPath === '/login/reg'){
if(this.$refs.sonLogin.$data.userName || this.$refs.sonLogin.$data.userPassword){
alert("您输入的信息将要被清空,确定要离开吗")
this.$refs.sonLogin.$data.userName = ""
this.$refs.sonLogin.$data.userPassword = ""
}
}else if(to.fullPath === '/login/log'){
if(this.$refs.sonLogin.$data.userName || this.$refs.sonLogin.$data.userPassword || this.$refs.sonLogin.$data.repeatPassword || this.$refs.sonLogin.$data.verityCount){
alert("您输入的信息将要被清空,确定要离开吗")
this.$refs.sonLogin.$data.userName = ""
this.$refs.sonLogin.$data.userPassword = ""
this.$refs.sonLogin.$data.repeatPassword = ""
this.$refs.sonLogin.$data.verityCount = ""
}
}
next()
}
}
4、校验用户登录时,使用全局守卫,死循环问题
校验用户是否登录,你可能会这样写:
router.beforeEach((to,from,next)=>{
if("登录"){
next()
}else{
next({path:"/login"})
}
}
判断是否登录过,否则直接跳转到登录页面;看似没有什么问题,但是执行的时候,浏览器直接进入死循环了!原因在于:
当没有登陆进入else时,它直接会跳转到登录页面, 而当进入登录页面时,还会执行全局前置守卫,这个时候还是未登录状态,会再次执行else代码块,直接进入了一个死循环,所以浏览器直接卡死了!!!
解决方法:
先说下我的思路,只要有token,那么我就让你跳转到任何页,否则,再加一层判断,用户是否是导航到登录页面,是的话next(),否则 next( { path :" /login " } )自动跳转到 登录页。
这样就不会涉及到死循环的问题了
router.beforeEach((to,from,next)=>{
if(token){
next()
}else{
if(to.name == "play"){
next()
}else{
next({path:"/play"})
}
}
})
5、可复用组件
在可复用组件中,我们如何观测到期变化呢?下边我介绍三种方法
1、使用组件内守卫—— beforeRouteUpdate( to,from,next ) =>{ }
2、使用 watch:{ '$route'( to,from ) { } } 来观测变化
3、在路由出口 <router-view :key="$route.fullPath"/> 标签上动态添加key,因为每次切换路由 $route.fullPath 的路径都会不一样,组件间的状态会变成 不断销毁 与不断创建的过程。。。(生命周期钩子也将执行)
6、关于组件内守卫beforeRouteEnter
这几天做项目,有个需求是这样的:
登录和注册页面是渲染的同一组件,子组件通过props接受父组件传来的不同内容,以便渲染登录页面还是注册页面,本来想通过下边watch 来监听路径的变化,从而传递不同的内容,但是watch只会监听可复用组件间的切换,如果从一个普通组件切换到可复用组件(比如:从首页------>登录页),不会触发watch:
watch:{
'$route'(to,from){
if(to.fullPath == '/login'){
// ···
}else{
// ···
}
}
},
后来又想了想,在 组件内守卫—— beforeRouteUpdate( to,from,next ) =>{ }也不行,因为它也是只有可复用组件切换时才触发,最后想到可以在 组件内守卫—— beforeRouteEnter( to,from,next ) =>{ } 来判断,不管是从普通组件----->登录页,还是登录页与注册页之间的切换,都会执行beforeRouteEnter( to,from,next ) =>{ }这个守卫,下边使用next中的回调函数解决此问题,判断切换到的是注册页还是登录页面,渲染不同内容:
export default {
name: 'Login',
data(){
return{
style:{},
veritycount:""
}
},
beforeRouteEnter(to,from,next){
next((_this)=>{
if(_this.$route.fullPath == '/login/log'){
_this.style = {
title:'用户登录',
ishow:"none",
buttonContent:"登录",
isshowverity:"none"
}
}else{
_this.style = {
title:'用户注册',
ishow:"block",
buttonContent:"注册",
isshowverity:"block"
}
}
})
}
}
------------>
7、在created中请求后台数据,在mounted中获取不到
vue的生命周期钩子时同步执行的,而created请求的后台数据是异步请求,mounted中必然取不到,如果想操作获取到的数据,使用promise或 async await;当然你也可以使用watch来监听:
created() {
this.status = true
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({name: 'lxc'})
}, 3000)
}).then((res) => {
this.name = res.name
}).finally(() => {
this.status = false
})
}
在watch中监听:
created() {
new Promise((resolve, reject) => {
setTimeout(() => {
resolve({name: 'lxc'})
}, 3000)
}).then((res) => {
this.name = res.name
})
},
watch:{
name() {
this.name = 'xc'
}
}