使用beforeRouteUpdate 和beforeRouteLeave 解决路由前置钩子需要与实例通信的问题
前景提要
近期项目有个需求:为了增加用户粘性,增加菜单要可配置成需要登陆后才允许访问的功能。
最初的想法:在路由前置钩子中,判断目标路由是否需要登陆,不需要就直接跳转,否则弹出登录对话框。
然后问题来了:当初写登录的时候,没有写成一个组件,仅仅是常驻组件header 中的一个对话框,通过一个布尔loginFlag 判断是否打开对话框。然而,在全局前置钩子中无法访问组件实例this ,无法通过事件总线与对话框取得通信,也无法通过route 跳转到登录页。
解决过程
经过查找资料、与朋友探讨和学习,了解到“组件内守卫”,
beforeRouteEnter(to, from) {
},
beforeRouteUpdate(to, from) {
},
beforeRouteLeave(to, from) {
},
由于项目的登录对话框是写在header 中的,看起来很符合beforeRouteUpdate 的使用场景,无论路由如何改变,header 都是复用的,并且还可以访问组件实例this ,岂不是可以直接操作loginFlag 来打开登录对话框了。
但是如官网文档描述的:
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave 守卫。 - 调用全局的
beforeEach 守卫。 - 在重用的组件里调用
beforeRouteUpdate 守卫(2.2+)。 - 在路由配置里调用
beforeEnter 。 - 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter 。 - 调用全局的
beforeResolve 守卫(2.5+)。 - 导航被确认。
- 调用全局的
afterEach 钩子。 - 触发 DOM 更新。
- 调用
beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
在调用beforeRouteUpdate 的时候,已经执行过全局的前置钩子beforeEach 了,为了避免用户直接关掉对话框继续访问需登录的内容。在beforeRouteUpdate 中需要跳转回前一个页面(目前没想到更好的方法处理这个问题)。
beforeRouteUpdate(to, from, next) {
let token = getToken()
if (to.meta.needLogin && !token) {
next()
this.$router.push({
name: from.name,
query: from.query,
params: from.params
})
this.loginFlag = true
} else {
next()
}
},
可能存在的问题
beforeRouteUpdate 和beforeRouteLeave 两个钩子放在子组件中可能不生效。需要写在父组件中才生效。
参考资料
组件内守卫
|