NavigationGuard - 导航守卫接口
NavigationGuard 接口定义了路由导航守卫函数的签名规范。导航守卫用于控制路由的访问权限、数据预加载和导航行为。🛡️
📋 接口定义
typescript
interface NavigationGuard {
(
to: RouteLocationNormalized,
from: RouteLocationNormalized,
next?: NavigationGuardNext
): NavigationGuardReturn
}1
2
3
4
5
6
7
2
3
4
5
6
7
🎯 功能说明
导航守卫就像是路由系统的"安全检查站",负责:
- 权限验证 - 检查用户是否有权限访问目标路由
- 数据预加载 - 在导航前加载必要的数据
- 导航控制 - 决定是否允许导航、重定向或取消
- 异步处理 - 支持异步操作和 Promise
🔧 参数详解
to - 目标路由位置
| 类型 | 说明 |
|---|---|
RouteLocationNormalized | 用户想要导航到的目标路由信息 |
from - 来源路由位置
| 类型 | 说明 |
|---|---|
RouteLocationNormalized | 用户当前所在的路由信息 |
next - 导航控制函数(可选)
| 类型 | 说明 |
|---|---|
NavigationGuardNext | 控制导航行为的回调函数(在旧式 API 中使用) |
📤 返回值
返回一个 NavigationGuardReturn 类型,可以是:
void- 继续导航(隐式调用next())boolean-true继续,false取消RouteLocationRaw- 重定向到新位置Error- 抛出导航错误Promise<NavigationGuardReturn>- 异步导航控制
💡 实际应用示例
基础权限守卫
javascript
const authGuard: NavigationGuard = (to, from) => {
// 检查路由是否需要认证
if (to.meta.requiresAuth && !isAuthenticated()) {
// 重定向到登录页
return '/login'
}
// 允许导航继续
return true
}
// 注册全局前置守卫
router.beforeEach(authGuard)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
异步数据预加载
javascript
const dataPreloadGuard: NavigationGuard = async (to, from) => {
// 检查是否需要预加载数据
if (to.meta.requiresData) {
try {
// 异步加载数据
await preloadRouteData(to)
return true // 继续导航
} catch (error) {
// 加载失败,重定向到错误页面
return '/error'
}
}
return true
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
复杂的业务逻辑守卫
javascript
const businessLogicGuard: NavigationGuard = (to, from) => {
// 检查用户角色
const userRole = getUserRole()
const requiredRole = to.meta.requiredRole
if (requiredRole && userRole !== requiredRole) {
// 角色不匹配,显示无权限页面
return {
path: '/unauthorized',
query: { from: to.fullPath }
}
}
// 检查页面访问频率限制
if (isRateLimited(to.path)) {
// 访问过于频繁,取消导航
return false
}
// 所有检查通过,允许导航
return true
}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
🎯 守卫类型
全局守卫
javascript
// 全局前置守卫
router.beforeEach((to, from) => {
console.log(`从 ${from.path} 导航到 ${to.path}`)
return true
})
// 全局后置钩子
router.afterEach((to, from, failure) => {
if (!failure) {
trackPageView(to.fullPath)
}
})
// 全局解析守卫
router.beforeResolve(async (to, from) => {
// 在所有组件内守卫和异步路由组件被解析之后调用
await ensureDataLoaded(to)
})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
路由独享守卫
javascript
const routes = [
{
path: '/admin',
component: AdminPanel,
beforeEnter: (to, from) => {
// 仅针对这个路由的守卫
if (!isAdmin()) {
return '/login'
}
return true
}
}
]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
组件内守卫
vue
<script>
export default {
beforeRouteEnter(to, from, next) {
// 在渲染该组件的对应路由被验证前调用
// 不能获取组件实例 `this`
next(vm => {
// 通过 `vm` 访问组件实例
vm.loadData()
})
},
beforeRouteUpdate(to, from) {
// 在当前路由改变,但是该组件被复用时调用
// 可以访问组件实例 `this`
this.userId = to.params.id
this.loadUserData()
},
beforeRouteLeave(to, from) {
// 在导航离开渲染该组件的对应路由时调用
// 可以访问组件实例 `this`
if (this.hasUnsavedChanges) {
return confirm('有未保存的更改,确定要离开吗?')
}
}
}
</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
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
🔧 高级用法
守卫组合和链式调用
javascript
// 创建可组合的守卫函数
function createGuardChain(...guards) {
return async (to, from) => {
for (const guard of guards) {
const result = await guard(to, from)
if (result !== true) {
return result // 中断链式调用
}
}
return true // 所有守卫都通过
}
}
// 使用组合守卫
const combinedGuard = createGuardChain(
authGuard,
permissionGuard,
dataPreloadGuard
)
router.beforeEach(combinedGuard)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
异步守卫的错误处理
javascript
const errorHandlingGuard: NavigationGuard = async (to, from) => {
try {
// 执行异步操作
await performAsyncCheck(to)
return true
} catch (error) {
// 记录错误并重定向
console.error('导航守卫错误:', error)
return {
path: '/error',
query: {
message: '导航过程中发生错误',
code: error.code
}
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
守卫的元编程
javascript
// 基于路由元数据的动态守卫
function createMetaBasedGuard(metaKey, handler) {
return (to, from) => {
if (to.meta[metaKey]) {
return handler(to, from, to.meta[metaKey])
}
return true
}
}
// 使用示例
const featureFlagGuard = createMetaBasedGuard(
'featureFlag',
(to, from, flagName) => {
return isFeatureEnabled(flagName) ? true : '/feature-disabled'
}
)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
🚨 注意事项
避免无限重定向
javascript
// ❌ 错误:可能导致无限循环
const badGuard = (to, from) => {
if (to.path === '/login') {
return '/login' // 无限重定向到自身
}
return true
}
// ✅ 正确:添加终止条件
const goodGuard = (to, from) => {
if (to.path === '/login' && isAuthenticated()) {
return '/' // 已认证用户重定向到首页
}
return true
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
异步守卫的性能考虑
javascript
// 避免在守卫中执行昂贵的同步操作
const optimizedGuard = async (to, from) => {
// 使用缓存避免重复计算
if (shouldCheckCache(to)) {
const cachedResult = getFromCache(to.fullPath)
if (cachedResult !== undefined) {
return cachedResult
}
}
// 异步操作
const result = await performAsyncOperation(to)
setCache(to.fullPath, result)
return result
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
🔗 相关接口
NavigationGuardReturn- 守卫返回值类型RouteLocationNormalized- 标准化路由位置
💡 专业建议:保持导航守卫的简洁和专注,每个守卫只负责一个特定的检查逻辑。复杂的业务逻辑应该拆分成多个简单的守卫组合使用。