导航守卫 | Vue Router
概述
导航守卫是 Vue Router 的核心功能之一,用于控制路由导航。它们允许你在路由切换的不同阶段执行逻辑,如权限验证、数据预加载、页面访问控制等。
守卫类型
Vue Router 提供了三种主要类型的导航守卫:
- 全局守卫 - 应用于所有路由
- 路由独享守卫 - 应用于特定路由
- 组件内守卫 - 在组件内部使用
全局守卫
beforeEach - 全局前置守卫
在路由导航开始前触发,适合进行权限验证。
javascript
const router = createRouter({ ... })
router.beforeEach((to, from, next) => {
// 检查是否需要认证
if (to.meta.requiresAuth && !isAuthenticated()) {
// 重定向到登录页
next('/login')
} else {
// 继续导航
next()
}
})1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
beforeResolve - 全局解析守卫
在所有组件内守卫和异步路由组件被解析之后触发。
javascript
router.beforeResolve(async (to, from) => {
// 数据预加载
if (to.meta.requiresData) {
await preloadRequiredData(to)
}
})1
2
3
4
5
6
2
3
4
5
6
afterEach - 全局后置钩子
在导航完成后触发,适合进行日志记录、页面统计等。
javascript
router.afterEach((to, from) => {
// 页面访问统计
analytics.trackPageView(to.fullPath)
// 更新页面标题
if (to.meta.title) {
document.title = `${to.meta.title} - 我的应用`
}
})1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
路由独享守卫
beforeEnter
在路由配置中定义,只对特定路由生效。
javascript
const routes = [
{
path: '/admin',
component: AdminPanel,
beforeEnter: (to, from, next) => {
if (!isAdmin()) {
next('/access-denied')
} else {
next()
}
}
}
]1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
组件内守卫
选项式 API
javascript
export default {
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被验证前调用
next(vm => {
// 通过 `vm` 访问组件实例
})
},
beforeRouteUpdate(to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
next()
},
beforeRouteLeave(to, from, next) {
// 导航离开该组件的对应路由时调用
next()
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
组合式 API
vue
<script setup>
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
// 路由离开守卫
onBeforeRouteLeave((to, from, next) => {
if (hasUnsavedChanges.value) {
const answer = confirm('有未保存的更改,确定要离开吗?')
if (answer) {
next()
} else {
next(false)
}
} else {
next()
}
})
// 路由更新守卫
onBeforeRouteUpdate(async (to, from, next) => {
if (to.params.id !== from.params.id) {
await loadUserData(to.params.id)
}
next()
})
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
实际应用场景
场景 1:用户认证系统
javascript
// 认证守卫
router.beforeEach((to, from, next) => {
const isAuthRequired = to.meta.requiresAuth
const isAuthenticated = checkAuthStatus()
if (isAuthRequired && !isAuthenticated) {
// 保存目标路由,登录后重定向
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else if (to.path === '/login' && isAuthenticated) {
// 已登录用户访问登录页,重定向到首页
next('/')
} else {
next()
}
})
// 登录组件
const Login = {
methods: {
async login() {
await auth.login(this.credentials)
const redirect = this.$route.query.redirect || '/'
this.$router.push(redirect)
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
场景 2:权限管理系统
javascript
// 权限验证守卫
router.beforeEach((to, from, next) => {
const requiredPermissions = to.meta.permissions || []
if (requiredPermissions.length > 0) {
const userPermissions = getUserPermissions()
const hasPermission = requiredPermissions.every(perm =>
userPermissions.includes(perm)
)
if (!hasPermission) {
next('/forbidden')
return
}
}
next()
})
// 路由配置
const routes = [
{
path: '/admin',
component: AdminLayout,
meta: {
permissions: ['admin.access', 'user.manage']
},
children: [
{
path: 'users',
component: UserManagement,
meta: { permissions: ['user.manage'] }
}
]
}
]1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
场景 3:多步骤表单导航
javascript
// 表单步骤守卫
const formRoutes = [
{
path: '/form/step1',
component: Step1,
meta: { formStep: 1 }
},
{
path: '/form/step2',
component: Step2,
meta: { formStep: 2 },
beforeEnter: (to, from, next) => {
// 检查上一步是否完成
if (!formStore.isStepCompleted(1)) {
next('/form/step1')
} else {
next()
}
}
}
]
// 表单离开守卫
const FormComponent = {
beforeRouteLeave(to, from, next) {
if (this.hasUnsavedChanges && !this.isSubmitting) {
const answer = confirm('表单数据尚未保存,确定要离开吗?')
next(answer)
} else {
next()
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
高级用法
异步守卫链
javascript
// 异步守卫处理
async function executeGuardChain(to, from, guards) {
for (const guard of guards) {
const result = await guard(to, from)
if (result !== undefined) {
return result
}
}
return true
}
router.beforeEach(async (to, from, next) => {
const guards = [
authGuard,
permissionGuard,
featureFlagGuard,
maintenanceGuard
]
const result = await executeGuardChain(to, from, guards)
if (result === true) {
next()
} else if (typeof result === 'string' || typeof result === 'object') {
next(result)
} else {
next(false)
}
})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
守卫元编程
javascript
// 动态守卫注册系统
class GuardManager {
constructor(router) {
this.router = router
this.guards = new Map()
}
registerGuard(name, guard) {
this.guards.set(name, guard)
this.updateGlobalGuard()
}
unregisterGuard(name) {
this.guards.delete(name)
this.updateGlobalGuard()
}
updateGlobalGuard() {
// 移除旧的全局守卫
this.router.beforeEach(() => {})
// 注册新的组合守卫
this.router.beforeEach(async (to, from, next) => {
for (const [name, guard] of this.guards) {
try {
const result = await guard(to, from)
if (result !== undefined) {
next(result)
return
}
} catch (error) {
console.error(`守卫 ${name} 执行失败:`, error)
next(false)
return
}
}
next()
})
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
最佳实践
1. 守卫组织策略
javascript
// 按功能模块组织守卫
const authGuards = [
requireAuth,
checkSession,
validateToken
]
const businessGuards = [
checkSubscription,
validatePermissions,
enforceRateLimit
]
// 组合守卫
router.beforeEach(async (to, from, next) => {
const guards = [...authGuards, ...businessGuards]
for (const guard of guards) {
const result = await guard(to, from)
if (result) {
next(result)
return
}
}
next()
})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2. 错误处理
javascript
// 守卫错误处理
router.beforeEach(async (to, from, next) => {
try {
// 执行守卫逻辑
await executeGuards(to, from)
next()
} catch (error) {
console.error('导航守卫执行失败:', error)
// 根据错误类型处理
if (error instanceof AuthenticationError) {
next('/login')
} else if (error instanceof PermissionError) {
next('/forbidden')
} else {
next('/error')
}
}
})1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
3. 性能优化
javascript
// 守卫缓存和优化
const guardCache = new Map()
function createCachedGuard(guard) {
return async (to, from) => {
const cacheKey = `${to.fullPath}-${from.fullPath}`
if (guardCache.has(cacheKey)) {
return guardCache.get(cacheKey)
}
const result = await guard(to, from)
guardCache.set(cacheKey, result)
// 设置缓存过期时间
setTimeout(() => {
guardCache.delete(cacheKey)
}, 5000)
return result
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
注意事项
- 守卫顺序 - 理解不同守卫的执行顺序
- 异步处理 - 正确处理异步守卫和 Promise
- 性能影响 - 避免在守卫中执行昂贵操作
- 错误边界 - 为守卫添加适当的错误处理
🛡️ 总结:导航守卫是 Vue Router 的强大功能,合理使用可以构建出安全、可靠的单页应用程序。