路由导航守卫变化
路由导航守卫是Vue Router的核心功能之一,它允许开发者在路由切换的不同阶段注入逻辑,实现权限控制、数据预加载等场景。随着Vue 2到Vue 3的升级,导航守卫的API和行为也发生了显著变化。
导航守卫的基本类型
Vue Router提供了三种主要导航守卫:
-
全局守卫:作用于所有路由
router.beforeEach
router.beforeResolve
router.afterEach
-
路由独享守卫:在路由配置中定义
beforeEnter
-
组件内守卫:在组件选项中定义
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
// 全局前置守卫示例
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !store.state.isAuthenticated) {
next('/login')
} else {
next()
}
})
Vue 2中的导航守卫
在Vue 2中,导航守卫主要通过函数参数中的next
方法控制流程:
// Vue 2风格的守卫
beforeRouteEnter(to, from, next) {
getData(to.params.id, (data) => {
next(vm => {
vm.data = data
})
})
}
典型特点包括:
- 必须调用
next()
继续流程 beforeRouteEnter
是唯一无法访问this
的守卫- 错误处理通过
next(error)
实现
Vue 3中的重大变化
Vue 3引入了Composition API,导航守卫也相应进行了现代化改造:
1. 返回值替代next
现在可以直接返回布尔值或路由位置:
router.beforeEach((to, from) => {
if (to.meta.requiresAuth && !auth.isAuthenticated()) {
return '/login'
}
// 隐式返回undefined等同于调用next()
})
2. 异步守卫支持
可以更自然地使用async/await:
router.beforeEach(async (to) => {
if (to.meta.requiredData) {
try {
await store.dispatch('fetchInitialData')
} catch (err) {
return false // 中止导航
}
}
})
3. 组件内守卫的Composition API
在setup()中使用onBeforeRouteUpdate等函数:
import { onBeforeRouteLeave } from 'vue-router'
export default {
setup() {
onBeforeRouteLeave((to, from) => {
if (formData.value.isDirty) {
return confirm('确定要离开吗?')
}
})
}
}
导航解析流程的变化
Vue 3优化了导航解析顺序:
- 触发失活组件的
beforeRouteLeave
- 调用全局
beforeEach
- 在重用的组件中调用
beforeRouteUpdate
- 调用路由配置中的
beforeEnter
- 解析异步路由组件
- 调用激活组件的
beforeRouteEnter
- 调用全局
beforeResolve
- 导航确认
- 调用全局
afterEach
常见问题解决方案
1. 权限控制模式
// 动态路由场景下的权限处理
router.beforeEach(async (to) => {
if (!auth.initialized) {
await auth.initialize()
}
if (to.meta.roles && !auth.hasAnyRole(to.meta.roles)) {
return { path: '/403', query: { originalPath: to.fullPath } }
}
})
2. 数据预加载策略
// 路由独享守卫中的数据预取
const routes = [{
path: '/user/:id',
component: UserDetail,
async beforeEnter(to) {
const user = await fetchUser(to.params.id)
to.meta.user = user // 传递给组件
}
}]
3. 滚动行为集成
router.afterEach((to) => {
if (to.meta.scrollToTop) {
window.scrollTo({ top: 0 })
}
})
性能优化技巧
- 守卫去重:避免在全局守卫中重复相同逻辑
- 懒加载优化:配合路由懒加载使用守卫
- 缓存策略:对频繁访问的路由实施缓存
// 缓存已验证路由
const routeCache = new Set()
router.beforeEach((to) => {
if (routeCache.has(to.path)) return true
if (needsValidation(to)) {
return validate(to).then(() => {
routeCache.add(to.path)
})
}
})
与Pinia的状态集成
在Vue 3生态中,与Pinia配合使用的模式:
import { useAuthStore } from '@/stores/auth'
router.beforeEach((to) => {
const auth = useAuthStore()
if (to.meta.requiresAdmin && !auth.isAdmin) {
return { name: 'unauthorized' }
}
})
测试导航守卫
使用Vue Test Utils测试守卫的示例:
import { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'
test('blocks unauthorized access', async () => {
const router = createRouter({
history: createWebHistory(),
routes: [{ path: '/admin', meta: { requiresAuth: true } }]
})
router.beforeEach((to) => {
if (to.meta.requiresAuth) return '/login'
})
await router.push('/admin')
expect(router.currentRoute.value.path).toBe('/login')
})
调试技巧
- 使用路由元信息标记调试:
router.beforeEach((to) => {
console.log('[Navigation]', to.meta.guardDebug ?
`Debug: ${to.name}` :
`Navigating to ${to.path}`)
})
- 开发环境专用守卫:
if (import.meta.env.DEV) {
router.beforeEach(logNavigation)
}
迁移注意事项
从Vue 2迁移到Vue 3时需注意:
- 移除所有显式的
next()
调用 - 转换回调风格为Promise/async风格
- 更新组件内守卫到Composition API
- 检查依赖导航顺序的副作用
// 迁移前 (Vue 2)
beforeRouteLeave(to, from, next) {
if (this.hasChanges) {
next(confirm('Discard changes?'))
} else {
next()
}
}
// 迁移后 (Vue 3)
onBeforeRouteLeave(() => {
return !hasChanges.value || confirm('Discard changes?')
})
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn