全局守卫

全局守卫会在每次导航时触发,适合实现跨路由的通用逻辑,例如权限验证、登录检查和页面分析。

router.beforeEach()

router.beforeEach() 注册一个全局前置守卫,每当导航触发时都会按照注册顺序依次执行。

签名

ts
/**
 * 注册全局前置守卫
 * @param guard - 守卫函数
 * @returns 注销函数
 */
router.beforeEach((to, from) => GuardResult): () => void

守卫返回值

守卫函数的返回值决定了导航的后续行为:

返回值行为
void / true放行导航
false拦截导航,状态为 aborted
NavTarget / RouteIndex重定向到指定路由
Promise等待 Promise 解析后根据结果决定行为

拦截导航

返回 false 可以阻止导航:

ts
router.beforeEach((to, from) => {
  if (to.path === '/admin' && !isLoggedIn()) {
    return false
  }
})

重定向

返回一个导航目标可以将用户重定向到其他页面:

ts
router.beforeEach((to, from) => {
  if (to.path === '/admin' && !isLoggedIn()) {
    return '/login'
  }
})

也支持返回导航选项对象:

ts
router.beforeEach((to, from) => {
  if (!isLoggedIn()) {
    return { index: '/login', query: { redirect: to.fullPath } }
  }
})

异步守卫

当守卫需要执行异步操作(如请求接口验证权限)时,可以返回一个 Promise:

ts
router.beforeEach(async (to, from) => {
  if (to.path.startsWith('/admin')) {
    const hasPermission = await checkPermission()
    if (!hasPermission) {
      return '/login'
    }
  }
})

多个守卫的执行顺序

你可以注册多个前置守卫,它们会按照注册顺序依次执行:

ts
router.beforeEach(guard1) // 先执行
router.beforeEach(guard2) // 后执行

如果任意一个守卫返回 false 或重定向,后续守卫将不再执行。

在 createRouter 中配置

除了调用 router.beforeEach(),你也可以在 createRouter 的选项中直接传入守卫数组:

ts
const router = createRouter({
  mode: 'hash',
  routes: [...],
  beforeEach: [authGuard, logGuard]
})

WARNING

使用 createRouter()createWebRouter() 创建路由实例时,路由器会自动初始化并执行首次导航。由于初始化在构造过程中同步完成,实例化后通过 router.beforeEach() 注册的守卫不会在首次导航时触发

如果需要守卫在首次导航时生效,有两种方式:

  1. createRouter 选项中配置beforeEach(推荐)
  2. 使用 createWebRouter(options, false) 禁用自动初始化,注册守卫后再手动调用 router.init()
ts
// 方式一:在选项中配置(推荐)
const router = createRouter({
  routes: [...],
  beforeEach: authGuard
})

// 方式二:手动初始化
const router = createWebRouter({ routes: [...] }, false)
router.beforeEach(authGuard)
router.init()

router.afterEach()

router.afterEach() 注册一个全局后置钩子,在导航完成后执行。与前置守卫不同,后置钩子无法改变导航结果。

签名

ts
/**
 * 注册全局后置钩子
 * @param hook - 钩子函数
 * @returns 注销函数
 */
router.afterEach((to, from) => void): () => void

典型用途

后置钩子常用于以下场景:

  • 页面分析:记录页面访问数据
  • 页面标题:根据路由更新文档标题
  • 进度条:关闭导航加载进度条
ts
router.afterEach((to, from) => {
  // 更新页面标题
  document.title = to.meta.title || '默认标题'

  // 上报页面访问
  analytics.trackPageView(to.fullPath)
})

WARNING

afterEach 钩子在导航确认后执行,此时路由状态已经更新。不要在这里执行可能影响导航的逻辑。

完整示例

ts
import { createRouter } from 'vitarx-router'

const router = createRouter({
  mode: 'hash',
  routes: [
    { path: '/', component: Home },
    { path: '/login', component: Login },
    { path: '/dashboard', component: Dashboard, meta: { requiresAuth: true } },
    { path: '/admin', component: Admin, meta: { requiresAuth: true, requiresAdmin: true } }
  ]
})

// 全局前置守卫:权限验证
router.beforeEach(async (to, from) => {
  // 不需要认证的页面直接放行
  if (!to.meta.requiresAuth) {
    return
  }

  // 检查是否已登录
  const isLoggedIn = await checkLoginStatus()
  if (!isLoggedIn) {
    // 未登录则重定向到登录页,并携带原始目标路径
    return { index: '/login', query: { redirect: to.fullPath } }
  }

  // 检查是否需要管理员权限
  if (to.meta.requiresAdmin) {
    const isAdmin = await checkAdminStatus()
    if (!isAdmin) {
      return '/dashboard'
    }
  }
})

// 全局后置钩子:页面标题和访问统计
router.afterEach((to, from) => {
  document.title = to.meta.title ?? 'My App'
  analytics.trackPageView(to.fullPath)
})

路由独享守卫 — 了解如何为特定路由配置专属守卫。