面试之谈谈路由导航

134 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第3天,点击查看活动详情

导航守卫

官方的解释是:vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航

我个人的理解是:vue-router是路由的监控,在路由跳转的不同阶段,执行各个时期相对应的操作。举一个最简单的例子,当用户进入页面时,未登录就跳转到登录页,已登录就正常进入。

路由导航的分类

全局路由、单个路由独享守卫、组件级别的守卫。

全局守卫

全局路由守卫又分为三种:

  • 全局前置守卫 router.beforeEach()
  • 全局解析守卫 router.beforeResolve()
  • 全局后置钩子 router.afterEach()

先来介绍一下我demo的结构

image.png

全局前置守卫

全局前置守卫在路由刚开始导航且还未进入路由对应的组件中时触发,简单来说即最早触发,但是触发时候没有任何组件等加载,正因为如此适合做登录判断逻辑。每次路由跳转,router.beforeEach都会执行

const router = new Router({})
router.beforeEach((to.from,next) => {
    // 这里写明逻辑判断
    return false
})

router.beforeEach()有三个参数:(下面的演示是从home跳转about)

  • to: 导航去的路由地址,是一个对象object

    to = {
        name: "about",
        meta: {},
        path: "/about",
        hash: "",
        query: {},
        params: {},
        fullPath: "/about",
        matched: Array(1)  {path: '/about', regex: /^\/about(?:\/(?=$))?$/i, components: {…}, alias: Array(0), instances: {…}, …}
    }
    
  • from: 导航来的路由地址,也是一个对象object

    from = {
        name: null,
        meta: {},
        path: "/",
        hash: "",
        query: {},
        params: {},
        fullPath: "/",
        matched: Array(0) 
    }
    
  • next: 可选参数

    next() 表示直接放行

    next({ name: 'Login' }) 表示跳转到Login页面

    next(false) 中断当前的导航,如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。

    // 下面是我打印的next
    next: function (to) {
        if (to === false) {
          // next(false) -> abort navigation, ensure current URL
          this$1$1.ensureURL(true);
          abort(createNavigationAbortedError(current, route));
        } else if (isError(to)) {
          this$1$1.ensureURL(true);
          abort(to);
        } else if (typeof to === 'string' || typeof to === 'object' && (typeof to.path === 'string' || typeof to.name === 'string')) {
          // next('/') or next({ path: '/' }) -> redirect
          abort(createNavigationRedirectedError(current, route));
          if (typeof to === 'object' && to.replace) {
            this$1$1.replace(to);
          } else {
            this$1$1.push(to);
          }
        } else {
          // confirm transition and pass on the value
          next(to);
        }
      }
    

全局解析守卫

全局解析守卫和全局前置守卫类似,因为它在每次导航时都会触发,但是确保在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被正确调用

router.beforeResolve((to, from, next) =>{
    console.log('这是全局解析守卫')
})

全局后置钩子

全局后置钩子只接受to和from,不会接受 next 函数也不会改变导航本身。它对于分析、更改页面标题、声明页面等辅助功能以及许多其他事情都很有用。

router.afterEach((to, from) => {
    console.log('这是全局后置钩子') 
})

路由独享守卫

beforeEnter 守卫 只在进入路由时触发,不会在 paramsquery 或 hash 改变时触发。

const routes = [
  {
    path: "/about", 
    component: () => import('@/views/AboutView.vue'), 
    beforeEnter: (to, from, next) => { 
        console.log('这是路由独享的守卫')
    }
  },
]

组件级别的守卫

组件级别的守卫又称为组件内守卫,类似于生命周期一共有三个:

  • beforeRouteEnter:进入该路由时执行
  • beforeRouteUpdate:该路由中参数改变时执行
  • beforeRouteLeave:离开该路由时执行。
const UserDetails = {
  template: `...`,
  beforeRouteEnter(to, from) {
    // 在渲染该组件的对应路由被验证前调用
    // 不能获取组件实例 `this` !
    // 因为当守卫执行时,组件实例还没被创建!
  },
  beforeRouteUpdate(to, from) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 `/users/:id`,在 `/users/1` 和 `/users/2` 之间跳转的时候,
    // 由于会渲染同样的 `UserDetails` 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 因为在这种情况发生的时候,组件已经挂载好了,导航守卫可以访问组件实例 `this`
  },
  beforeRouteLeave(to, from) {
    // 在导航离开渲染该组件的对应路由时调用
    // 与 `beforeRouteUpdate` 一样,它可以访问组件实例 `this`
  },
}

完整的导航解析流程

  1. 导航被触发。
  2. 在失活的组件里调用beforeRouteLeave守卫。
  3. 调用全局的beforeEach守卫。
  4. 在重用的组件里调用beforeRouteUpdate守卫(2.2+)。
  5. 在路由配置里调用beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用beforeRouteEnter
  8. 调用全局的beforeResolve守卫(2.5+)。
  9. 导航被确认。
  10. 调用全局的afterEach钩子。
  11. 触发 DOM 更新。
  12. 调用beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。

总结

Vue的路由导航有三种守卫,分别是全局守卫、路由独享守卫和组件内守卫,全局守卫包括全局前置守卫router.beforeEach()、全局解析守卫router.beforeResolve()、全局后置钩子router.afterEach();路由独享守卫包括router.beforeEnter();组件内内守卫包括beforeRouteEnter()、beforeRouteUpdate()、beforeRouteLeave()。

路由导航的执行顺序:导航被触发、失活组件调用beforeRouteLeave(清除组件内定时器)、调用全局前置守卫beforeEach(登录状态的判断决定是否跳转到登录页)、重用组件的beforeRouteUpdate、路由独享组件beforeEnter、解析异步路由组件、被激活组件的beforeRouteEnter、全局解析守卫beforeResolve、导航被确认、调用全局后置钩子afterEach、DOM更新