Skip to content

NavigationGuard - 导航守卫接口

NavigationGuard 接口定义了路由导航守卫函数的签名规范。导航守卫用于控制路由的访问权限、数据预加载和导航行为。🛡️

📋 接口定义

typescript
interface NavigationGuard {
  (
    to: RouteLocationNormalized,
    from: RouteLocationNormalized,
    next?: NavigationGuardNext
  ): NavigationGuardReturn
}

🎯 功能说明

导航守卫就像是路由系统的"安全检查站",负责:

  1. 权限验证 - 检查用户是否有权限访问目标路由
  2. 数据预加载 - 在导航前加载必要的数据
  3. 导航控制 - 决定是否允许导航、重定向或取消
  4. 异步处理 - 支持异步操作和 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)

异步数据预加载

javascript
const dataPreloadGuard: NavigationGuard = async (to, from) => {
  // 检查是否需要预加载数据
  if (to.meta.requiresData) {
    try {
      // 异步加载数据
      await preloadRouteData(to)
      return true // 继续导航
    } catch (error) {
      // 加载失败,重定向到错误页面
      return '/error'
    }
  }
  
  return true
}

复杂的业务逻辑守卫

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
}

🎯 守卫类型

全局守卫

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)
})

路由独享守卫

javascript
const routes = [
  {
    path: '/admin',
    component: AdminPanel,
    beforeEnter: (to, from) => {
      // 仅针对这个路由的守卫
      if (!isAdmin()) {
        return '/login'
      }
      return true
    }
  }
]

组件内守卫

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>

🔧 高级用法

守卫组合和链式调用

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)

异步守卫的错误处理

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 
      }
    }
  }
}

守卫的元编程

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'
  }
)

🚨 注意事项

避免无限重定向

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
}

异步守卫的性能考虑

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
}

🔗 相关接口

💡 专业建议:保持导航守卫的简洁和专注,每个守卫只负责一个特定的检查逻辑。复杂的业务逻辑应该拆分成多个简单的守卫组合使用。

🚀 Vue Router - 让前端路由变得简单而强大 | 构建现代化单页应用的最佳选择