了解 VueRouter 导航守卫

213 阅读5分钟

导航守卫是什么?

官方介绍:导航守卫主要用来通过跳转或取消的方式守卫导航
有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的

简单来说:导航守卫就是路由跳转过程中的一些钩子函数,路由跳转是一个大的过程
这个大的过程分为跳转前中后等小的过程,在每一个过程中都有一函数,这个函数能让你操作一些其他的事儿的时机

全部的导航守卫分三种(全局的、单个路由独享的、组件内)

全局前置守卫    beforeEach        当一个导航触发时,按照创建顺序调用
全局解析守卫    beforeResolve     在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后被调用
全局后置钩子    afterEach         进入路由之后被调用,不能改变路由跳转
路由独享的守卫  beforeEnter       在路由配置中直接定义,与前置守卫的方法参数一样
组件内的守卫    beforeRouteEnter  在渲染该组件的对应路由被confirm前调用,不能获取组件实例this
               beforeRouteUpdate 在当前路由改变,但是该组件被复用时调用
               beforeRouteLeave  导航离开该组件的对应路由时调用,可以访问组件实例 this

全局前置守卫(router.beforeEach 注册全局前置守卫)

const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
  // ...
})
当一个导航触发时,全局前置守卫按照创建顺序调用
守卫是异步解析执行,此时导航在所有守卫resolve完之前一直处于等待中

每个守卫方法接收三个参数
to: Route          即将要进入的目标路由对象
from: Route        当前导航正要离开的路由
next: Function     一定要调用该方法来resolve这个钩子,执行效果依赖next方法的调用参数

next的参数
next()             进行管道中的下一个钩子,如果全部钩子执行完了,则导航的状态就是确认的
next(false)        中断当前的导航,如果浏览器的URL改变了那么URL地址会重置到from路由对应的地址
next('/') 或 next({ path: '/' })   跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航
next(error)        如果传入的是Error实例,则导航会被终止且该错误会被传递给router.onError() 注册过的回调

确保next函数在任何给定的导航守卫中都被过调用一次
它可以多次出现,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错

全局解析守卫(router.beforeResolve 注册全局解析守卫)

在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
和beforeEach类似,也是路由跳转前触发,参数也是to,from,next三个
在beforeEach和组件内beforeRouteEnter之后,afterEach之前调用

全局后置钩子(afterEach 注册全局后置钩子)

和beforeEach相反,它是在路由跳转完成后触发,不会改变导航本身
参数包括to,from没有了next,它发生在beforeEach和beforeResolve之后,beforeRouteEnter之前

router.afterEach((to, from) => {
  // ...
})

路由独享的守卫(beforeEnter 注册路由独享的守卫)

是指在单个路由配置的时候也设置的钩子函数,其位置就是下面示例中的位置
像Foo这样的组件都存在这样的钩子函数。目前它只有一个钩子函数beforeEnter
和beforeEach完全相同,如果都设置则在beforeEach之后紧随执行,参数tofromnext

const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

组件内的钩子函数

直接在路由组件内定义:beforeRouteEnter,beforeRouteUpdate、beforeRouteLeave
  1. beforeRouteEnter
进入组件前被调用,此时组件实例还没有被创建,所以函数内部不能访问this
如果需要访问当前vue实例,可以通过next() 方法的回调函数接收一个vm实例

访问当前vue实例的属性
export default {
  data() {
    return {
      msg: '消息'
    }
  },
  beforeRouteEnter(to, from, next) {
    next(vm => {                    // 访问当前vue实例
      console.log(vm.msg)
    })
  }
}

2.beforeRouteUpdate

在路由发生变化,但是组件被复用时被调用

父路由监听子路由变化
routes: [{                         // router.js
    path: '/',
    name: 'Home',
    component: Home,
    children: [{
        path: 'goods',
        name: 'Goods',
        component: Goods,
    }, {
        path: 'about',
        name: 'About',
        component: About,
    }]
}]

export default {                   // Home.vue
    beforeRouteUpdate(to, from, next) {
        console.log('子路由发生变化')
        next()
    }
}
当访问地址在 http://localhost:8080/#/goods 和 http://localhost:8080/#/about 间切换时
会触发Home.vue组件的 beforeRouteUpdate 方法。

在访问 http://localhost:8080/#/goods/1 和 http://localhost:8080/#/goods/2 切换地址时
由于路由地址的变化只表示参数发生变化,页面组件并没有变化,也会执行 beforeRouteUpdate 钩子函数

3.beforeRouteLeave

离开组件时被调用,可以用来阻止页面离开,或者离开前销毁定时器等

页面离开前清除定时器
export default {
  beforeRouteLeave (to, from, next) {
    window.clearInterval(this.timer)    //清除定时器
      next()
  }
}

完整的导航解析流程

导航被触发
在失活的组件里调用 beforeRouteLeave 守卫
调用全局的 beforeEach 守卫
在重用的组件里调用 beforeRouteUpdate 守卫
在路由配置里调用 beforeEnter
解析异步路由组件
在被激活的组件里调用 beforeRouteEnter
调用全局的 beforeResolve 守卫
导航被确认
调用全局的 afterEach 钩子
触发 DOM 更新
调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入