全局守卫
全局守卫会在每次导航时触发,适合实现跨路由的通用逻辑,例如权限验证、登录检查和页面分析。
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() 注册的守卫不会在首次导航时触发。
如果需要守卫在首次导航时生效,有两种方式:
- 在
createRouter选项中配置beforeEach(推荐) - 使用
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)
})路由独享守卫 — 了解如何为特定路由配置专属守卫。