一、前言
在Vue3开发中,路由守卫是一个非常重要的知识点。很多常见需求都离不开它,比如:
-
页面权限控制
-
登录校验
-
动态路由参数校验
-
页面离开提醒
-
路由切换时执行额外逻辑
路由守卫本质上就是:在路由跳转的不同阶段,执行指定逻辑,对跳转过程进行控制。
本文重点整理三类最常用的守卫:
-
全局路由守卫
-
路由独享守卫
-
组件内守卫
尽量聚焦在"是什么、怎么用、有什么作用"
二、什么是路由守卫
路由守卫可以理解为一种"拦截机制"。
当用户准备从一个页面跳转到另一个页面时,Vue Router 会在跳转前、跳转后,或者组件更新过程中,给我们一些可以插入逻辑的时机。我们可以在这些时机中:
-
放行跳转
-
拦截跳转
-
重定向到其他页面
-
执行提示、日志、校验等逻辑
简单说,路由守卫解决的是:
页面不是想进就进,也不是想走就走,而是先经过规则判断。
三、路由守卫的分类
Vue Router 中常见的路由守卫主要有三类:
1. 全局守卫
对所有路由都生效,适合处理整个项目通用的逻辑。
2. 路由独享守卫
只对某一个具体路由生效,适合处理该路由特有的逻辑。
3. 组件内守卫
写在页面组件内部,适合处理和当前组件强相关的逻辑。
可以先记住一句话:
全局守卫管整体,独享守卫管单个路由,组件内守卫管当前页面组件。
四、全局路由守卫
1. 什么是全局前置守卫
全局前置守卫会在每次路由跳转前执行,常用写法如下:
TypeScript
router.beforeEach((to, from, next) => {
next()
})
参数说明:
-
to:准备进入的目标路由
-
from:当前离开的路由
-
next:控制是否继续跳转
它最常见的作用就是:
-
登录校验
-
权限控制
-
根据路由信息做统一判断
2. 全局前置守卫的典型用法
一个很常见的场景是:某些页面必须登录后才能访问。
示例:
TypeScript
router.beforeEach((to, from, next) => {
if (to.meta.login) {
const token = localStorage.getItem('token')
if (!token) {
next('/login')
return
}
}
next()
})
这段代码的逻辑是:
-
每次跳转前先进入守卫
-
判断目标路由是否需要登录
-
如果需要登录,就去本地检查token
-
没有token就跳转到登录页
-
有token就正常放行
这里的重点不是代码本身,而是理解这种设计思路:
把通用的权限判断统一放到全局前置守卫里处理。
3.meta的作用
在做权限控制时,通常会结合路由元信息meta一起使用,例如:
TypeScript
{
path: '/admin',
name: 'admin',
component: Admin,
meta: {
login: true,
title: '后台管理'
}
}
这里的meta 可以理解为:给路由额外附加的自定义信息。
例如:
-
login:true 表示这个页面需要登录
-
title:可以用来设置页面标题
好处是:
-
规则和路由配置写在一起,结构清晰
-
全局守卫可以统一读取这些信息
-
后期扩展角色权限也比较方便
4. 全局后置守卫
全局后置守卫会在路由跳转完成后执行,写法如下:
TypeScript
router.afterEach((to, from) => {
console.log('路由跳转完成')
})
它的常见用途有:
-
记录日志
-
页面埋点统计
-
修改文档标题
注意两点:
-
afterEach没有next
-
它不能阻止跳转
所以它更适合做"跳转完成之后的附加处理"。
五、路由独享守卫
1. 什么是路由独享守卫
路由独享守卫是只对某个路由生效的守卫,直接写在路由配置对象中,常用写法如下:
TypeScript
{
path: '/article/:id',
name: 'article',
component: Article,
beforeEnter(to, from, next) {
next()
}
}
它适合处理某一个页面自己的进入规则,而不是整个项目的通用规则。
2. 路由独享守卫的典型作用
最常见的用途之一就是:进入页面前先校验路由参数。
例如文章详情页通常会写成:
TypeScript
{
path: '/article/:id',
name: 'article',
component: Article,
beforeEnter(to, from, next) {
const id = Number(to.params.id)
if (isNaN(id)) {
next(from)
return
}
next()
}
}
这里的意思是:
-
id本来应该是数字
-
如果用户手动输入了错误地址,比如/article/xxx
-
那么就不让它正常进入详情页
这就是路由独享守卫非常典型的使用方式:
只为某一个路由,增加进入前的专属校验。
3. 为什么这里不用全局守卫
因为这个参数校验只对文章详情页有效,不适用于其他页面。
如果强行写到全局守卫里,会带来两个问题:
-
逻辑变杂,职责不清
-
其他页面也会无意义地经过这段判断
所以这类"只属于某个页面"的规则,更适合写在 beforeEnter 中。
六、组件内守卫
组件内守卫写在页面组件内部,适合处理和当前页面组件密切相关的逻辑。
Vue Router里比较常见的两个组合式 API 写法是:
-
onBeforeRouteUpdate -
onBeforeRouteLeave
1. onBeforeUpdate的作用
它会在当前路由更新,但组件被复用时触发。
示例:
TypeScript
import { onBeforeRouteUpdate } from 'vue-router'
onBeforeRouteUpdate((to, from, next) => {
console.log('路由参数变化时触发')
next()
})
这个守卫特别适合详情页场景。
比如当前在:
TypeScript
/article/1
然后跳转到:
TypeScript
/article/2
这时很多情况下并不会重新创建一个新组件,而是复用原来的组件实例。也就是说,虽然地址变了,但页面组件还是同一个。
这时候如果你只在组件初次加载时处理数据,很可能拿不到更新后的参数,因此就需要 onBeforeRouteUpdate。
它的作用可以概括为:
-
监听同一组件下的路由变化
-
在参数变化时重新处理逻辑
-
适合重新请求详情数据
2. onBeforeRouteLeave的作用
它会在当前组件对应的路由即将离开时触发。
示例:
TypeScript
import { onBeforeRouteLeave } from 'vue-router'
onBeforeRouteLeave((to, from, next) => {
console.log('路由离开时触发')
next()
})
它常见的作用有:
-
离开表单页前提醒用户
-
未保存内容时阻止离开
-
离开页面前做一些清理工作
例如:
TypeScript
onBeforeRouteLeave((to, from, next) => {
const leave = window.confirm('当前内容未保存,确定离开吗?')
if (!leave) return
next()
})
这类需求在后台管理系统和表单页面里非常常见。
七、三类守卫的区别总结
| 守卫类型 | 写在哪里 | 作用范围 | 常见用途 |
|---|---|---|---|
| 全局守卫 | 路由实例上 | 所有路由 | 登录校验、权限控制、埋点 |
| 路由独享守卫 | 单个路由配置中 | 当前路由 | 参数校验、页面准入 |
| 组件内守卫 | 页面组件内部 | 当前组件 | 参数变化、离开提醒 |
简而言之就是:
-
通用规则,用全局守卫
-
单页规则,用路由独享守卫
-
页面内部行为,用组件内守卫
八、容易混淆的点
1. 不是所有逻辑都应该写到全局守卫里
全局守卫适合放"所有页面都可能会用到"的规则。 如果某段逻辑只对一个页面有效,就应该考虑写成路由独享守卫或组件内守卫。
2. 动态路由参数变了,不代表组件一定会重新创建
例如:
-
/article/1
-
/article/2
这两个地址看起来变了,但它们可能对应的仍然是同一个组件实例。
3. next()不能忘记
在使用这类守卫时,如果仍采用next这种写法,那么就必须正确调用它。
常见情况:
-
next():放行 -
next('/login'):重定向 -
不调用:导航可能被卡住
所以写守卫时要有明确意识:到底是放行、拦截,还是跳去别的页面。
九、常见应用场景总结
1. 登录校验
适合用全局前置守卫。
2. 动态参数校验
适合用路由独享守卫。
3. 切换同类详情页重新加载数据
适合用组件内更新守卫。
4. 离开页面前提醒
适合用组件内离开守卫。
5. 页面跳转后记录日志
适合用全局后置守卫。
十、Review
1. 全局前置守卫
TypeScript
router.beforeEach((to, from, next) => {
next()
})
关键词:所有路由、进入前、统一控制
2. 全局后置守卫
TypeScript
router.afterEach((to, from) => {})
关键词:跳转后、日志、埋点
3. 路由独享守卫
TypeScript
beforeEnter(to, from, next) {
next()
}
关键词:单个路由专属
4. 组件内更新守卫
TypeScript
onBeforeRouteUpdate((to, from, next) => {
next()
})
关键词:参数变化、组件复用
5. 组件内离开守卫
TypeScript
onBeforeRouteLeave((to, from, next) => {
next()
})
关键词:离开页面前确认
十一、总结
路由守卫的核心不是死记它的API,而是应该先理解"它解决什么问题":
-
全局守卫解决通用规则问题
-
路由独享守卫解决单一路由规则问题
-
组件内守卫解决当前页面内部过程控制问题
把这三类守卫的定位分清楚,后面无论是做登录权限、详情页参数校验,还是页面离开提醒,都会更容易上手。
总而言之,就是:
先判断这是"全局问题、单页问题,还是组件内部问题",再决定用哪一种守卫。