Setting the file. One moment.
Skill 15 · Vue Router Best Practices
Subchapter 15.1
reference/router-beforeenter-no-param-trigger.mdMarkdown5 KBView on GitHub
Impact: MEDIUM - The beforeEnter guard defined in route configuration only triggers when entering a route from a DIFFERENT route. Changes to params, query strings, or hash within the same route do NOT trigger , potentially bypassing important validation logic.
beforeEnteronBeforeRouteUpdate for param/query changesbeforeEach with route.params/query checks// router.js
const routes = [
{
path: '/orders/:id',
component: OrderDetail,
beforeEnter: async (to, from) => {
// This runs when entering from /products
// But NOT when navigating from /orders/1 to /orders/2!
const order = await checkOrderAccess(to.params.id)
if (!order.canView) {
return '/unauthorized'
}
}
}
]Scenario:
/products to /orders/1 - beforeEnter runs, access checked/orders/1 to /orders/2 - beforeEnter DOES NOT run!| Navigation | beforeEnter fires? |
|---|---|
/products → /orders/1 | YES |
/orders/1 → /orders/2 | NO |
/orders/1 → /orders/1?tab=details | NO |
/orders/1#section → /orders/1#other | NO |
/orders/1 → /products → /orders/2 | YES (leaving and re-entering) |
<!-- OrderDetail.vue -->
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
// Handle param changes within the same route
onBeforeRouteUpdate(async (to, from) => {
if (to.params.id !== from.params.id) {
const order = await checkOrderAccess(to.params.id)
if (!order.canView) {
return '/unauthorized'
}
}
})
</script>// router.js
router.beforeEach(async (to, from) => {
// Handle all order access checks globally
if (to.name === 'OrderDetail') {
// This runs on EVERY navigation to this route, including param changes
const order = await checkOrderAccess(to.params.id)
if (!order.canView) {
return '/unauthorized'
}
}
})// router.js - For entering from different route
const routes = [
{
path: '/orders/:id',
component: OrderDetail,
beforeEnter: (to) => validateOrderAccess(to.params.id)
}
]
// In component - For param changes within route
// OrderDetail.vue
onBeforeRouteUpdate((to) => validateOrderAccess(to.params.id))
// Shared validation function
async function validateOrderAccess(orderId) {
const order = await checkOrderAccess(orderId)
if (!order.canView) {
return '/unauthorized'
}
}// guards/orderGuards.js
export const orderAccessGuard = async (to) => {
const order = await checkOrderAccess(to.params.id)
if (!order.canView) {
return '/unauthorized'
}
}
// router.js
const routes = [
{
path: '/orders/:id',
component: OrderDetail,
beforeEnter: [orderAccessGuard] // Can add multiple guards
}
]
// Still need in-component guard for param changes!Understanding when each guard type fires:
1. beforeRouteLeave (in-component, leaving component)
2. beforeEach (global)
3. beforeEnter (per-route, ONLY when entering from different route)
4. beforeRouteEnter (in-component, entering component)
5. beforeResolve (global)
6. afterEach (global, after navigation confirmed)
For param/query changes on same route:
1. beforeRouteUpdate (in-component) - ONLY this fires!
2. beforeEach (global)
3. beforeResolve (global)
4. afterEach (global)