vue-router钩子函数的生命周期

场景描述

近日碰到了一个项目需求,使我不得不去研究vue-router里路由切换的具体过程,在这个过程中发生了什么事情。

场景大概如下:在一个表单页面里,用户可以选择提交,也可以选择不提交而离开。产品经理要求数据不保存在本地浏览器里,在用户提交成功后销毁表单数据;或者用户离开页面(点击取消或者其他tab)时,出现弹窗,提醒用户表单数据还未提交,是否放弃。

在这个场景里,难点在于:1. 如何监听用户离开页面或者关闭页面的事件; 2.如何在用户选择放弃提交以后还能正确指向之前点击的路由目标。

'场景截图'

如上图所示,用户可以点击取消,或者任何侧边dashboard的tab,这就说明,modal弹窗的“取消”按钮上不能增加固定的路由链接,因为无法确定用户会点击哪个路径。只能通过先记录点击路径,然后绑定到取消按钮上。

轮子分析

1.在轮子中解决

首先,内置location对象是可以使用的,但造成的问题是与vue-router分离性太强,同时也无法做到简洁的数据绑定。因此,最佳的方案还是在vue-router本身的功能中解决。
在之前我对于vue-router的了解并不多,一般只是用来做一些基本的路由控制和页面跳转传参,类似如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
var Foo = {
template: '<p>This is foo!</p>'
}
var Bar = {
template: '<p>This is bar!</p>'
}
var App = {}
// 创建一个路由器实例
var router = new VueRouter()
// 定义路由规则
router.map({
'/foo': {
component: Foo
},
'/bar': {
component: Bar
}
})
// 路由器会创建一个 App 实例,并且挂载到选择符 #app 匹配的元素上。
router.start(App, '#app')

同时会用到一些路由嵌套,参数传递,大多数情况下,这些功能已经足够覆盖很多项目需求了。

2.vue-router钩子函数生命周期

搞懂了vue路由里钩子函数的运转过程,就可以轻松解决各种跳转需求。
路由的切换过程,本质上是执行一系列路由钩子函数,钩子函数总体上分为两大类:

  • 全局的钩子函数
  • 组件的钩子函数

全局的钩子函数定义在全局的路由对象中,组件的钩子函数则定义在组件的route选项中。

全局钩子函数

全局钩子函数有2个:

  • beforeEach:在路由切换开始时调用
  • afterEach:在每次路由切换成功进入激活阶段时被调用

组件钩子函数

组件的钩子函数一共6个:

  • data:可以设置组件的data
  • activate:激活组件
  • deactivate:禁用组件
  • canActivate:组件是否可以被激活
  • canDeactivate:组件是否可以被禁用
  • canReuse:组件是否可以被重用

切换对象

每个切换钩子函数都会接受一个 transition 对象作为参数。这个切换对象包含以下函数和方法:

  • transition.to
    表示将要切换到的路径的路由对象。
  • transition.from
    代表当前路径的路由对象。
  • transition.next()
    调用此函数处理切换过程的下一步。
  • transition.abort([reason])
    调用此函数来终止或者拒绝此次切换。
  • transition.redirect(path)
    取消当前切换并重定向到另一个路由。

具体的分析过程可以查看官方文档,也可以看这位仁兄的详细教学

附一张详细教学里的生命周期图片:

'生命周期'

3.实战解决

首先,对我目前的这一个组件绑定一个新的data:completeSubmit。根据这个data的布尔值来判断是否应该跳出询问的弹窗。

1
2
3
4
5
6
7
8
9
10
11
//页面跳转的时候,需要进行询问。
deactivate: function (transition) {
if(!this.completeSubmit){
//未完成提交,出现提示信息
this.confirmModalJump = true;
transition.abort();
}
else{
transition.next();
}
}

出现弹窗的时候,一定要加transition.abort()这行代码,这样router就不会继续前进,地址栏的url也不会进行变化。

那么,如何记录用户即将前往的页面router呢。我们此时先把transition打印出来看一下具体的内容:

transition对象

可以看到,transition里就有关于目的地的记录。那么只要在组件的data里绑定一个新的数据就可以进行记录了。

1
2
3
4
5
6
7
8
9
10
11
12
//页面跳转的时候,需要进行询问。
deactivate: function (transition) {
this.toPage = transition.to.name;
if(!this.completeSubmit){
//未完成提交,出现提示信息
this.confirmModalJump = true;
transition.abort();
}
else{
transition.next();
}
}

通过this.toPage我们就可以拿到目的地了。

modal组件里,通过$dispatch来传递事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
export default {
data () {
return {
...
}
},
methods: {
leaveConfirm: function(){
this.$dispatch('confirmLeave', true)
},
cancleBtn: function(){
this.$dispatch('clickDeleteLayer', true)
}
}
}

然后在本组件里进行事件的接收

1
2
3
4
5
6
7
8
9
10
11
events: {
'confirmLeave': function(){
this.confirmModalJump = false;
this.completeSubmit = true;
this.$route.router.go({ name: this.toPage});
window.localStorage.removeItem('addServerInfo');
},
'clickDeleteLayer': function(){
this.confirmModalJump = false;
}
}

在用户确定离开的时候,我们首先把modal隐藏,接着把this.completeSubmit的布尔值进行更改(否则router部分的transition无法进行next()),然后通过this.toPage拿到之前用户的点击地址,在router的对象里进行操作。

关于router的编码式导航,请一定仔细看文档,并区分vue-router的版本号。2.0版本和0.7版本的写法有明显不同。但二者都是支持编码式导航的(另一种方式是常见的html标签式导航)。

最后,清空用户的填写记录,就大功告成啦。

Snapline wechat
扫码关注我的公众号“约翰柠檬的唱片店”
Buy me a cup of Coffee