大家好,我是小杨,一个在Vue项目中被路由守卫"救过命"的前端老司机。今天咱们来聊聊Vue项目中那个既强大又容易踩坑的功能——路由守卫。这玩意儿就像是你网站的保安系统,用好了能挡掉各种非法访问,用不好可能连自己都进不去!
1. 路由守卫是啥?先看个真实案例
去年我做了一个后台管理系统,上线后客户说:"怎么谁都能进管理员页面?" 当时我就懵了,赶紧加上了路由守卫:
router.beforeEach((to, from, next) => {
if (to.path.startsWith('/admin') && !store.state.isAdmin) {
next('/login') // 不是管理员?门口等着!
} else {
next() // 自己人,请进~
}
})
就这么几行代码,解决了大问题!这就是路由守卫的威力。
2. 三种守卫类型:门卫、房间管家和清洁工
Vue路由提供了三种守卫,各司其职:
2.1 全局守卫(大门保安)
beforeEach
:进门前检查afterEach
:进门后记录
// 记录页面访问
router.afterEach((to, from) => {
analytics.trackPageView(to.path) // 打个卡
})
2.2 路由独享守卫(房间管家)
只在特定路由生效:
{
path: '/dashboard',
component: Dashboard,
beforeEnter: (to, from, next) => {
if (!store.state.user) {
next('/login') // 没带门卡不让进
} else {
next()
}
}
}
2.3 组件内守卫(房间内的规则)
直接在组件里定义:
export default {
beforeRouteEnter(to, from, next) {
// 还不能访问this,因为组件实例还没创建
next(vm => {
// 现在可以通过vm访问组件实例
})
},
beforeRouteUpdate(to, from, next) {
// 当前路由改变,但该组件被复用时调用
this.fetchData(to.params.id) // 比如重新获取数据
next()
},
beforeRouteLeave(to, from, next) {
// 离开前检查
if (this.hasUnsavedChanges) {
if (confirm('有未保存的更改,确定离开?')) {
next()
} else {
next(false) // 取消导航
}
} else {
next()
}
}
}
3. 我的血泪教训:守卫中的异步操作
有一次我在beforeRouteEnter
里写了个异步请求:
// ❌ 错误示范
beforeRouteEnter(to, from, next) {
api.checkPermission().then(res => {
if (res.valid) {
next()
} else {
next('/denied')
}
})
}
结果页面卡住不动了!后来才明白,beforeRouteEnter
的next函数不会等待Promise。正确做法:
// ✅ 正确做法
beforeRouteEnter(to, from, next) {
next(vm => {
vm.checkPermission() // 在组件实例创建后调用
})
}
4. 权限控制实战:RBAC方案
在最近的项目中,我实现了一套基于角色的权限控制:
// 权限检查函数
function checkPermission(to, user) {
const requiredRole = to.meta.role
if (!requiredRole) return true
return user.roles.includes(requiredRole)
}
// 全局守卫
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !store.state.user) {
return next('/login')
}
if (!checkPermission(to, store.state.user)) {
return next('/403') // 无权限页面
}
next()
})
对应的路由配置:
{
path: '/admin',
meta: {
requiresAuth: true,
role: 'ADMIN'
}
}
5. 常见问题解答
Q:为什么我的守卫执行了两次?
A:可能是路由重复注册或者守卫里有重定向循环
Q:如何取消导航?
A:调用next(false)
即可
Q:路由变化但组件没更新?
A:试试在beforeRouteUpdate
里处理数据更新
6. 性能优化小技巧
守卫里不要放重逻辑!我曾经见过这样的代码:
// ❌ 性能杀手
beforeEach(async (to, from, next) => {
const user = await fetchUser() // 每次路由变化都请求用户信息
// ...
})
应该改成:
// ✅ 优化方案
let userPromise = null
router.beforeEach(async (to, from, next) => {
if (!userPromise) {
userPromise = fetchUser()
}
const user = await userPromise
// ...
})
总结
路由守卫是Vue项目中的瑞士军刀,能处理:
- 权限控制
- 数据预取
- 页面访问记录
- 表单未保存提示
- 等等...
记住几个原则:
- 全局守卫处理通用逻辑
- 路由独享守卫处理特定规则
- 组件内守卫处理组件相关逻辑