不只懂 Vue 语法:试解释如何使用导航守卫?

问题回答

导航守卫(Navigation Guard)可以在 3 个地方使用,包括全域、元件和路由。所谓导航守卫就是在访问页面之前,会像一个守卫拦截并执行你所设定的任务,执行後会跳转或取消访问该路由。常用情况例如是登入验证,意思是在用户载入页面前,验证此用户的 token 是否有效,有就放行,没有就导回登入页。另外,导航守卫有不同的 Hook 函式可使用,就如 Vue 也有它的生命周期(createdmounted等等)。但注意,不同的守卫各自都有自己一套 Hook 方法。

注意:Vue 3 是使用 Vue Router 4.x。Vue 2 是使用 Vue Router 3.x。 两者某些语法会有不同。因此在查看文件时务必留意!

以下主要讲解 Vue 3 (Vue Router 4.x)的做法。今天主题比较直白,的确没有什麽概念要理解,但也会举出自己曾经使用过的例子作解说。

全域守卫

每次进入任何一个路由前都会作拦截,并执行你在导航守卫里写下的任务。这些函式同样会以非同步方式执行。

执行次序:

  1. router.beforeEach()
    每次进入任何一个路由前,都会作呼叫。

使用情景:身分验证。

  1. router.beforeResolve()
    beforeEach() 一样,在进入路由前呼叫。但它会在 beforeEach() 後,并且在所有路由、元件的导航守卫都执行完,最後才会呼叫并执行。

使用情景:呼叫 API,取得远端资料。

  1. router.afterEach()

使用情景:像 GA 追踪工具一样记录使用者浏览纪录。
结果跳转路由後才呼叫。

Hook 函式如下:

const router = createRouter({ ... })

router.beforeEach(async (to, from, next) => {
    // to:使用者要跳转的路由
    // from:使用者前一个访问的路由
    // 回传 false 取消跳转,true / undefined(预设)容许跳转
    return false
    
    // next 参数在 Vue Router 4 并非必须
})


router.beforeResolve(async (to, from, next) => {
    // do something...
})

router.afterEach((to, from, failure) => {
    // 因为 afterEach 是在路由跳转结束後才触发,因此没有 next
    // failure 参数表示路由跳转失败
})

注意:

  • 关於 next 参数,在 Vue Router 3 (即是 Vue 2 )是必须,否则 beforeEach 这个 hook 函式就不会被 resolve 并且报错。官方例子如下:
router.beforeEach((to, from, next) => {
  if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
  else next()
})

关於在 Vue Router 3 的 next 参数其他用法,详情参考官方文件

在 4.x 版本的 Vue Router 文件里,官方表示有机会移除 next 参数。因此不建议使用。

验证身份做法示范

这里简单示范一下自己使用导航守卫来验证身份的做法。我当时是透过在 cookies 存放使用者登入时所取得的 token,来证明此使用者已登入。换言之,如果在 cookies 里没有 token,或者 token 失效,代表此使用者目前没登入,并需要跳转回登入页。

以下例子,我使用了 js-cookie 插件来操作 cookie。

router / index.js

import Cookies from 'js-cookie';

consr routes = [
    {
        path: '/',
        component: Login // 首页是登入页
    },
    ...
]

const router = createRouter({
  history: createWebHashHistory(),
  routes,
});

// 使用全域导航守卫
// 每次跳转页面都检查是否有 cookies

router.beforeEach(to => {
    const token = Cookies.get('userToken');
    
    // 登入页(首页)不用验证
    if (to.fullPath === '/') return;
    
    if (!token || !validateToken(token)) {
      // 可能是因为 token 无效,所以需要移除此使用者的 token
      Cookies.remove('userToken');
      return '/';
    }

  }
  // 验证成功,可以放行
  return true;
});

注意无限循环问题

如果没有加 if (to.fullPath === '/') return;,就会出现死循环问题。因为跳转到首页时,守卫发现首页也没有验证成功,於是再次跳转到页面,造成无限循环:

[Vue Router warn]: Detected an infinite redirection in a navigation guard when going from "/" to "/". Aborting to avoid a Stack Overflow. This will break in production if not fixed.
[Vue Router warn]: Unexpected error when starting the router: Error: Infinite redirect in navigation guard at eval

路由独有守卫

只有在进入某一路由时,才会触发此导航守卫。 重点如下:

  • 只有 router.beforeEnter() 这 Hook 函式。
  • 只能在 route 物件里使用以上 Hook 函式。

官方例子:

const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: (to, from) => {
      // reject the navigation
      return false
    },
  },
]

注意:如果该路由的 params、query、hash 有变化,也不会触发守卫。例如 /user/1/users/2,或者 /user/1/#info/user/2/#detail

遇到要重用守卫的情况,可使用阵列。

官方例子:

const routes = [
  {
    path: '/users/:id',
    component: UserDetails,
    beforeEnter: [removeQueryParams, removeHash],
  },
  {
    path: '/about',
    component: UserDetails,
    beforeEnter: [removeQueryParams],
  },
]

元件守卫

在某元件设定守卫。元件守卫可使用的 Hook 函式:

  1. beforeRouteEnter()
    渲染元件前执行。不能使用 this
  2. beforeRouteUpdate()
    因为元件已挂载了,所以由此开始可以用 this
    路由改变时执行。
  3. beforeRouteLeave()
    离开元件时执行。

写法与在元件里使用生命周期一样:

data() {
    return { ... }
},
beforeRouteEnter(to, from, next) {
    // 无法使用 this
    // 但可以透过 next 使用 this
    next( vm => { ... })
},
beforeRouteUpdate(to, from) {
    // do something...
},
beforeRouteLeave(to, from) {
    // do something...
},

完整执行次序

Vue 官网有提到所有 Hook 的执行次序,在此就不重复了:

截图至官方文件

总结

  • 导航守卫有 3 种:全域、路由、元件守卫。各自有不同的 Hook 函式。
  • 通常利用全域守卫执行验证身份。
  • 全域:每次跳转页面都会执行守卫。
  • 路由、元件:在单一路由或元件执行守卫。
  • 路由的守卫只能在 route 物件里设定。

参考资料

重新认识 Vue.js - 4-4 路由守卫(Navigation Guards)
Vue Router - 导航守卫


<<:  [DAY18] Use Case 设计概念

>>:  Day18# Leetcode TwoSum

[Day23]solidity合约内容讲解part.1

因为它原本就会有一个storage是先帮你写好的,或许你第一次看到的时候会不知道这是甚麽,那我们会...

Day4 被动情蒐(1)-DNS、nslookup、host

被动情蒐目的:了解目标 针对渗透目标,收集公开来源的情报,了解目标的情形,这些资料可能来自受测企业...

第十八天:客制化 Gradle Task

虽然 Gradle 内建不少任务,也有众多 Plugin 可以增加更多常用任务。但毕竟每个专案都是独...

DAY27 Aidea专案实作-AOI瑕疵检测(2/4)

那我们要开始着手处理我们的资料集了,今天会先做资料前处理的部分,其实不管是机器学习或是深度学习,只要...

Day28 - Rails Resource 和 Model 入门

LINE Developers:https://developers.line.biz/zh-ha...