vue进阶:vue-router之导航守卫、路由元信息、路由懒加载

  • 1.导航被触发
  • 2.在失活的组件里调用离开守卫:beforeRouteLeave —— 组件内守卫(离开组件)。
  • 3.调用全局的beforeEach守卫 —— 全局守卫(进入组件)。
  • 4.在重用组件里调用deforeRouteUpdatar守卫(2.2+)—— 组件内守卫(组件复用时调用/foo/:id)。
  • 5.在路由配置里调用beforeEnter。—— 路由独享守卫(进入组件)。
  • 6.解析异步路由组件。
  • 7.在被激活的组件里调用beforeRouteEnter。—— 组件内守卫(进入组件)。
  • 8.调用全局的beforesolve守卫(2.5)。—— 全局解析守卫,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
  • 9.导航被确认。
  • 10.调用全局的afterEach钩子。——全局后置钩子(确定要进入的路由之后,vue实例创建之前,这个钩子只接收to,from两个参数,没有next参数)。
  • 11.触发DOM更新。

一、导航守卫

官方手册:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

vue-router的导航守卫在官方中有很详细的说明,应用也是比较简单,主要并不在于导航本身的应用,而是要理解导航守卫的所有API是在离开当前路由组件之后,进入新的路由组件之前被触发,在这个过程中来实现是否决定进入下一个路由组件或者留在当前路由组件,还是重定向到别的组件中,主要用于处理路由切换业务。导航守卫的业务必定会存在路由之间的数据操作,这时候就需要配置路由元信息来实现。

1.全局前置守卫:

1 const router = new VueRouter({ ... })
2 router.beforeEach((to, from, next) => {
3   // ...
4 })
  • to:Route:即将要进入的目标路由对象
  • from:Route:当前导航要离开的路由
  • next:function:最后调用该方法来resolve这个钩子(最终决定渠道那个路由组件),执行next方法需要的参数:
next() //去到目标路由对象
next(false) //中断导航,留在真要离开的from路由。如果浏览器的URL地址改变(用户手动或者浏览器后退按钮),那么url地址会重置到from路由对应的地址
next('/') 
next({path:'/'}) //跳转到指定的地址,当前导航中断,然后进入到一个新的导航
next(error) //2.4.0如果传入next的参数是一个Error实例,则导航会被终止且该错误会被传递给router.onError()注册过的回调。

要确保next()方法调用,不然钩子就不会被resolved。

2.其他守卫:

导航守卫API的参数几乎没有差别,基本上都使用“to,from,next”这三个参数,几乎vue实例在这些路由API中也不能用this获取,这时候获取的this基本上都是undefined,但也有例外:

router.beforeResolve((to,from,next) => {})//全局解析守卫 this指向undefined
router.afterEach((to,from) => {}) //全局后置守卫没有next参数,并且this指向也是undefined
beforeEnter:(to,from,next) => {} //路由独享守卫,this指向undefined
//组件内的守卫
beforeRouteEnter(to,from,next){} // 在渲染该组件的对应路由被 confirm 前调用; 不!能!获取组件实例 `this`; 因为当守卫执行前,组件实例还没被创建
beforeRouteUpdate(to,from,next){}// 在当前路由改变,但是该组件被复用时调用; 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候;由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用; 可以访问组件实例 `this`
beforeRouteLeave(to,from,next){}//导航离开该组件的对应路由时调用;可以访问组件实例 `this`
beforeRouteEnter不能获取this,但是能通过next传入一个vue实例来获取实例的一些数据。
1 beforeRouteEnter (to, from, next) {
2   next(vm => {
3     // 通过 `vm` 访问组件实例
4   })
5 }

但是注意beforeRouteUpdate和beforeRouteLeave中的next不能传入vue实例,因为这两个守卫已经可以通过this获取当前组件实例对象了。

 1 beforeRouteUpdate (to, from, next) {
 2   // just use `this`
 3   this.name = to.params.name
 4   next()
 5 }
 6 //
 7 beforeRouteLeave (to, from , next) {
 8   const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
 9   if (answer) {
10     next()
11   } else {
12     next(false)
13   }
14 }

二、路由元信息

从上一节的路由守卫的业务逻辑来看,必然会引出一个思考,就是怎么缓存路由访问权限,要不然每一次都需要进行权限验证,如果验证需要访问服务器的话,这样岂不是要消耗大量不必要的服务器资源,所以路由元信息就可以帮助我们来解决这个问题:

在定义路由的时候可以在路由中配置mata字段:

 1 const router = new VueRouter({
 2   routes: [
 3     {
 4       path: '/foo',
 5       component: Foo,
 6       children: [
 7         {
 8           path: 'bar',
 9           component: Bar,
10           // a meta field
11           meta: { requiresAuth: true }
12         }
13       ]
14     }
15   ]
16 })

meta字段可以通过$route对象来获取,但是$route上没有这个属性,因为$route.metched[n].meta来获取,$route.metched[n]缓存的是URL节点的路由配置数据,从第一个节点开始到当前节点按照顺序匹配到metched数组元素中。

 1 router.beforeEach((to, from, next) => {
 2   if (to.matched.some(record => record.meta.requiresAuth)) {
 3     // this route requires auth, check if logged in
 4     // if not, redirect to login page.
 5     if (!auth.loggedIn()) {
 6       next({
 7         path: '/login',
 8         query: { redirect: to.fullPath }
 9       })
10     } else {
11       next()
12     }
13   } else {
14     next() // 确保一定要调用 next()
15   }
16 })

简单的获取:

 this.loginFlag = this.$route.matched[0].meta.login;

三、路由懒加载

官方手册:https://router.vuejs.org/zh/guide/advanced/lazy-loading.html

将Router引入组件模块使用路由懒加载的方式加载:

const Home = () => import('./views/Home.vue')

路由懒加载还组合了预加载模式,当进入应用时首先加载首屏需要的组件模块进行渲染,剩下的组件模块会在网络空闲的时候预加载到缓存中,当需要到某个模块时直接调取缓存中的资源。