Routing navigation guard changes
Routing navigation guards are one of the core features of Vue Router, allowing developers to inject logic at different stages of route navigation to implement scenarios such as permission control and data preloading. With the upgrade from Vue 2 to Vue 3, the API and behavior of navigation guards have also undergone significant changes.
Basic Types of Navigation Guards
Vue Router provides three main types of navigation guards:
-
Global Guards: Apply to all routes
router.beforeEach
router.beforeResolve
router.afterEach
-
Per-Route Guards: Defined in route configurations
beforeEnter
-
In-Component Guards: Defined in component options
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
// Example of a global before guard
router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !store.state.isAuthenticated) {
next('/login')
} else {
next()
}
})
Navigation Guards in Vue 2
In Vue 2, navigation guards primarily control the flow using the next
method in function parameters:
// Vue 2-style guard
beforeRouteEnter(to, from, next) {
getData(to.params.id, (data) => {
next(vm => {
vm.data = data
})
})
}
Key characteristics include:
- Must call
next()
to continue the flow beforeRouteEnter
is the only guard that cannot accessthis
- Error handling is implemented via
next(error)
Major Changes in Vue 3
Vue 3 introduced the Composition API, and navigation guards were modernized accordingly:
1. Return Values Replace next
Now you can directly return boolean values or route locations:
router.beforeEach((to, from) => {
if (to.meta.requiresAuth && !auth.isAuthenticated()) {
return '/login'
}
// Implicitly returning undefined is equivalent to calling next()
})
2. Async Guard Support
More natural use of async/await:
router.beforeEach(async (to) => {
if (to.meta.requiredData) {
try {
await store.dispatch('fetchInitialData')
} catch (err) {
return false // Abort navigation
}
}
})
3. Composition API for In-Component Guards
Use functions like onBeforeRouteUpdate
in setup()
:
import { onBeforeRouteLeave } from 'vue-router'
export default {
setup() {
onBeforeRouteLeave((to, from) => {
if (formData.value.isDirty) {
return confirm('Are you sure you want to leave?')
}
})
}
}
Changes in Navigation Resolution Flow
Vue 3 optimized the navigation resolution order:
- Trigger
beforeRouteLeave
in deactivated components - Call global
beforeEach
- Call
beforeRouteUpdate
in reused components - Call
beforeEnter
in route configurations - Resolve async route components
- Call
beforeRouteEnter
in activated components - Call global
beforeResolve
- Confirm navigation
- Call global
afterEach
Common Problem Solutions
1. Permission Control Pattern
// Permission handling in dynamic routing scenarios
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. Data Preloading Strategy
// Data prefetching in per-route guards
const routes = [{
path: '/user/:id',
component: UserDetail,
async beforeEnter(to) {
const user = await fetchUser(to.params.id)
to.meta.user = user // Pass to component
}
}]
3. Scroll Behavior Integration
router.afterEach((to) => {
if (to.meta.scrollToTop) {
window.scrollTo({ top: 0 })
}
})
Performance Optimization Tips
- Deduplicate Guards: Avoid repeating the same logic in global guards
- Lazy Loading Optimization: Use guards with route lazy loading
- Caching Strategy: Implement caching for frequently accessed routes
// Cache validated routes
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)
})
}
})
Integration with Pinia State
Patterns for using with Pinia in the Vue 3 ecosystem:
import { useAuthStore } from '@/stores/auth'
router.beforeEach((to) => {
const auth = useAuthStore()
if (to.meta.requiresAdmin && !auth.isAdmin) {
return { name: 'unauthorized' }
}
})
Testing Navigation Guards
Example of testing guards with 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')
})
Debugging Tips
- Use route meta information for debugging:
router.beforeEach((to) => {
console.log('[Navigation]', to.meta.guardDebug ?
`Debug: ${to.name}` :
`Navigating to ${to.path}`)
})
- Development-only guards:
if (import.meta.env.DEV) {
router.beforeEach(logNavigation)
}
Migration Considerations
When migrating from Vue 2 to Vue 3, note the following:
- Remove all explicit
next()
calls - Convert callback-style to Promise/async-style
- Update in-component guards to the Composition API
- Check for side effects dependent on navigation order
// Before migration (Vue 2)
beforeRouteLeave(to, from, next) {
if (this.hasChanges) {
next(confirm('Discard changes?'))
} else {
next()
}
}
// After migration (Vue 3)
onBeforeRouteLeave(() => {
return !hasChanges.value || confirm('Discard changes?')
})
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn