阿里云主机折上折
  • 微信号
Current Site:Index > Routing navigation guard changes

Routing navigation guard changes

Author:Chuan Chen 阅读数:23901人阅读 分类: Vue.js

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:

  1. Global Guards: Apply to all routes

    • router.beforeEach
    • router.beforeResolve
    • router.afterEach
  2. Per-Route Guards: Defined in route configurations

    • beforeEnter
  3. 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 access this
  • 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:

  1. Trigger beforeRouteLeave in deactivated components
  2. Call global beforeEach
  3. Call beforeRouteUpdate in reused components
  4. Call beforeEnter in route configurations
  5. Resolve async route components
  6. Call beforeRouteEnter in activated components
  7. Call global beforeResolve
  8. Confirm navigation
  9. 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

  1. Deduplicate Guards: Avoid repeating the same logic in global guards
  2. Lazy Loading Optimization: Use guards with route lazy loading
  3. 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

  1. Use route meta information for debugging:
router.beforeEach((to) => {
  console.log('[Navigation]', to.meta.guardDebug ? 
    `Debug: ${to.name}` : 
    `Navigating to ${to.path}`)
})
  1. Development-only guards:
if (import.meta.env.DEV) {
  router.beforeEach(logNavigation)
}

Migration Considerations

When migrating from Vue 2 to Vue 3, note the following:

  1. Remove all explicit next() calls
  2. Convert callback-style to Promise/async-style
  3. Update in-component guards to the Composition API
  4. 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

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.