Vue Router导航守卫
Vue Router导航守卫是Vue Router提供的一种机制,允许开发者通过钩子函数在路由导航过程中进行拦截和控制。这些守卫可以用于权限验证、页面访问控制、数据预加载等场景,为单页应用(SPA)提供了更精细的路由控制能力。
导航守卫概述[编辑 | 编辑源代码]
导航守卫分为三类,按触发顺序排列如下:
1. 全局守卫:作用于所有路由 2. 路由独享守卫:只作用于特定路由 3. 组件内守卫:在路由组件内定义
导航解析流程可以用以下mermaid图表示:
全局守卫[编辑 | 编辑源代码]
全局守卫通过router实例直接注册,包含以下三种:
beforeEach[编辑 | 编辑源代码]
在导航触发前调用,常用于权限验证。
router.beforeEach((to, from, next) => {
// to: 即将进入的目标路由
// from: 当前导航正要离开的路由
// next: 必须调用此函数来resolve这个钩子
if (to.meta.requiresAuth && !isAuthenticated()) {
next('/login')
} else {
next() // 继续导航
}
})
beforeResolve[编辑 | 编辑源代码]
在导航被确认前调用,适合确保异步操作完成。
router.beforeResolve(async to => {
if (to.meta.fetchData) {
await fetchDataBeforeEnter(to.params.id)
}
})
afterEach[编辑 | 编辑源代码]
导航完成后调用,没有next参数。
router.afterEach((to, from) => {
logNavigation(to.path, from.path)
})
路由独享守卫[编辑 | 编辑源代码]
直接在路由配置中定义:
const routes = [
{
path: '/admin',
component: AdminPanel,
beforeEnter: (to, from, next) => {
if (user.role !== 'admin') {
next('/forbidden')
} else {
next()
}
}
}
]
组件内守卫[编辑 | 编辑源代码]
在路由组件内定义:
beforeRouteEnter[编辑 | 编辑源代码]
在渲染该组件的对应路由被确认前调用,不能访问组件实例(this)。
beforeRouteEnter(to, from, next) {
next(vm => {
// 通过vm访问组件实例
vm.loadData(to.params.id)
})
}
beforeRouteUpdate[编辑 | 编辑源代码]
在当前路由改变但组件被复用时调用。
beforeRouteUpdate(to, from, next) {
this.userId = to.params.id
this.fetchUserData()
next()
}
beforeRouteLeave[编辑 | 编辑源代码]
在导航离开该组件的对应路由时调用。
beforeRouteLeave(to, from, next) {
if (this.unsavedChanges) {
if (confirm('有未保存的更改,确定离开?')) {
next()
} else {
next(false)
}
} else {
next()
}
}
完整导航解析流程[编辑 | 编辑源代码]
1. 导航被触发 2. 调用即将离开组件的beforeRouteLeave 3. 调用全局的beforeEach 4. 在重用的组件里调用beforeRouteUpdate 5. 调用路由配置里的beforeEnter 6. 解析异步路由组件 7. 调用即将进入组件的beforeRouteEnter 8. 调用全局的beforeResolve 9. 导航被确认 10. 调用全局的afterEach 11. 触发DOM更新 12. 执行beforeRouteEnter中传给next的回调函数
实际应用案例[编辑 | 编辑源代码]
案例1:权限控制[编辑 | 编辑源代码]
router.beforeEach((to, from, next) => {
const publicPages = ['/login', '/register']
const authRequired = !publicPages.includes(to.path)
const loggedIn = localStorage.getItem('user')
if (authRequired && !loggedIn) {
return next('/login')
}
next()
})
案例2:页面访问统计[编辑 | 编辑源代码]
router.afterEach((to, from) => {
analytics.trackPageView(to.path)
})
案例3:动态标题[编辑 | 编辑源代码]
router.beforeEach((to, from, next) => {
document.title = to.meta.title || '默认标题'
next()
})
数学公式示例[编辑 | 编辑源代码]
如果需要计算导航耗时,可以使用以下公式:
其中:
- 是全局前置守卫执行时间
- 是路由独享守卫执行时间
- 是组件内守卫执行时间
- 是组件渲染时间
注意事项[编辑 | 编辑源代码]
1. 确保总是调用next(),否则钩子不会resolved 2. 在beforeRouteEnter中无法访问this,因为组件实例还未创建 3. 导航守卫是异步执行的 4. 可以使用next(false)中止当前导航 5. 可以使用next('/path')或next({ path: '/path' })重定向
通过合理使用导航守卫,开发者可以构建更安全、更高效的单页应用,实现复杂的路由控制逻辑。