Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vue Router 4 reference covering navigation guards, route params, lifecycle interactions, and common gotchas.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
reference/router-beforeenter-no-param-trigger.md
1---2title: Per-Route beforeEnter Guards Ignore Param/Query Changes3impact: MEDIUM4impactDescription: Route-level beforeEnter guards don't fire when only params, query, or hash change, causing unexpected bypasses of validation logic5type: gotcha6tags: [vue3, vue-router, navigation-guards, params, query]7---89# Per-Route beforeEnter Guards Ignore Param/Query Changes1011**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 `beforeEnter`, potentially bypassing important validation logic.1213## Task Checklist1415- [ ] Use in-component `onBeforeRouteUpdate` for param/query changes16- [ ] Or use global `beforeEach` with route.params/query checks17- [ ] Document which guards protect which scenarios18- [ ] Test navigation between same route with different params1920## The Problem2122```javascript23// router.js24const routes = [25{26path: '/orders/:id',27component: OrderDetail,28beforeEnter: async (to, from) => {29// This runs when entering from /products30// But NOT when navigating from /orders/1 to /orders/2!31const order = await checkOrderAccess(to.params.id)32if (!order.canView) {33return '/unauthorized'34}35}36}37]38```3940**Scenario:**411. User navigates from `/products` to `/orders/1` - beforeEnter runs, access checked422. User navigates from `/orders/1` to `/orders/2` - beforeEnter DOES NOT run!433. User might access order they don't have permission for!4445## What Triggers beforeEnter vs. What Doesn't4647| Navigation | beforeEnter fires? |48|------------|-------------------|49| `/products` → `/orders/1` | YES |50| `/orders/1` → `/orders/2` | NO |51| `/orders/1` → `/orders/1?tab=details` | NO |52| `/orders/1#section` → `/orders/1#other` | NO |53| `/orders/1` → `/products` → `/orders/2` | YES (leaving and re-entering) |5455## Solution 1: Add In-Component Guard5657```vue58<!-- OrderDetail.vue -->59<script setup>60import { onBeforeRouteUpdate } from 'vue-router'6162// Handle param changes within the same route63onBeforeRouteUpdate(async (to, from) => {64if (to.params.id !== from.params.id) {65const order = await checkOrderAccess(to.params.id)66if (!order.canView) {67return '/unauthorized'68}69}70})71</script>72```7374## Solution 2: Use Global beforeEach Instead7576```javascript77// router.js78router.beforeEach(async (to, from) => {79// Handle all order access checks globally80if (to.name === 'OrderDetail') {81// This runs on EVERY navigation to this route, including param changes82const order = await checkOrderAccess(to.params.id)83if (!order.canView) {84return '/unauthorized'85}86}87})88```8990## Solution 3: Combine Both Guards9192```javascript93// router.js - For entering from different route94const routes = [95{96path: '/orders/:id',97component: OrderDetail,98beforeEnter: (to) => validateOrderAccess(to.params.id)99}100]101102// In component - For param changes within route103// OrderDetail.vue104onBeforeRouteUpdate((to) => validateOrderAccess(to.params.id))105106// Shared validation function107async function validateOrderAccess(orderId) {108const order = await checkOrderAccess(orderId)109if (!order.canView) {110return '/unauthorized'111}112}113```114115## Solution 4: Use beforeEnter with Array of Guards116117```javascript118// guards/orderGuards.js119export const orderAccessGuard = async (to) => {120const order = await checkOrderAccess(to.params.id)121if (!order.canView) {122return '/unauthorized'123}124}125126// router.js127const routes = [128{129path: '/orders/:id',130component: OrderDetail,131beforeEnter: [orderAccessGuard] // Can add multiple guards132}133]134135// Still need in-component guard for param changes!136```137138## Full Navigation Guard Execution Order139140Understanding when each guard type fires:141142```1431. beforeRouteLeave (in-component, leaving component)1442. beforeEach (global)1453. beforeEnter (per-route, ONLY when entering from different route)1464. beforeRouteEnter (in-component, entering component)1475. beforeResolve (global)1486. afterEach (global, after navigation confirmed)149150For param/query changes on same route:1511. beforeRouteUpdate (in-component) - ONLY this fires!1522. beforeEach (global)1533. beforeResolve (global)1544. afterEach (global)155```156157## Key Points1581591. **beforeEnter is for route ENTRY only** - Not for within-route changes1602. **Use onBeforeRouteUpdate for param changes** - This is the in-component solution1613. **Global beforeEach always runs** - Good for centralized validation1624. **Test param change scenarios** - Easy to miss during development1635. **Consider security implications** - Param-based access control needs both guards164165## Reference166- [Vue Router Navigation Guards](https://router.vuejs.org/guide/advanced/navigation-guards.html)167- [Vue Router Per-Route Guards](https://router.vuejs.org/guide/advanced/navigation-guards.html#per-route-guard)168