[{"data":1,"prerenderedAt":2991},["ShallowReactive",2],{"page-\u002Fvue\u002F11-routing-vue-router":3},{"id":4,"title":5,"body":6,"description":2984,"extension":2985,"meta":2986,"navigation":64,"path":2987,"seo":2988,"stem":2989,"__hash__":2990},"content\u002Fvue\u002F11-routing-vue-router.md","Vue 3 Engineering Reference — Routing with Vue Router",{"type":7,"value":8,"toc":2974},"minimark",[9,14,19,687,691,1247,1251,1600,1604,1921,1925,2145,2149,2400,2404,2522,2526,2538,2728,2970],[10,11,13],"h1",{"id":12},"_11-routing-with-vue-router","11 — Routing with Vue Router",[15,16,18],"h2",{"id":17},"route-definition-lazy-loading-and-code-splitting","Route Definition — Lazy Loading and Code Splitting",[20,21,24],"code-wrapper",{"filename":22,"language":23},"router\u002Findex.ts","typescript",[25,26,30],"pre",{"className":27,"code":28,"language":23,"meta":29,"style":29},"language-typescript shiki shiki-themes github-light github-dark","import { createRouter, createWebHistory, type RouteRecordRaw } from 'vue-router'\n\n\u002F\u002F ── Dynamic import = code splitting ────────────────────\n\u002F\u002F Each route's component is a SEPARATE chunk, loaded on first navigation.\n\u002F\u002F Reduces initial bundle size — only the visited route's code downloads.\n\nconst routes: RouteRecordRaw[] = [\n  {\n    path: '\u002F',\n    name: 'home',\n    \u002F\u002F Eager import: part of the main bundle (critical for above-the-fold)\n    component: () => import('@\u002Fviews\u002FHomeView.vue'),\n  },\n  {\n    path: '\u002Fdashboard',\n    name: 'dashboard',\n    \u002F\u002F ── Chunk name via magic comment (for bundle analysis) ──\n    component: () => import(\u002F* webpackChunkName: \"dashboard\" *\u002F '@\u002Fviews\u002FDashboardView.vue'),\n    \u002F\u002F ── Meta: route-level metadata accessible in guards ──\n    meta: { requiresAuth: true, title: 'Dashboard' },\n    \u002F\u002F ── Nested routes: render in \u003CRouterView> inside the parent ──\n    children: [\n      {\n        path: 'settings',  \u002F\u002F matches \u002Fdashboard\u002Fsettings (no leading slash)\n        name: 'dashboard.settings',\n        component: () => import('@\u002Fviews\u002FSettingsView.vue'),\n      },\n      {\n        path: 'analytics',\n        name: 'dashboard.analytics',\n        component: () => import('@\u002Fviews\u002FAnalyticsView.vue'),\n      },\n    ],\n  },\n  {\n    \u002F\u002F ── Dynamic route params: \u002Fusers\u002F42 → params.id = \"42\" (string!) ──\n    path: '\u002Fusers\u002F:id',\n    name: 'user',\n    component: () => import('@\u002Fviews\u002FUserView.vue'),\n    \u002F\u002F Props mode: pass route params as component props (decouples from $route)\n    props: true,\n  },\n  {\n    \u002F\u002F ── Catch-all 404: must be LAST in the routes array ──\n    path: '\u002F:pathMatch(.*)*',\n    name: 'not-found',\n    component: () => import('@\u002Fviews\u002FNotFoundView.vue'),\n  },\n]\n\nexport const router = createRouter({\n  history: createWebHistory(import.meta.env.BASE_URL),\n  routes,\n  \u002F\u002F ── scrollBehavior: control scroll on navigation ──\n  scrollBehavior(to, from, savedPosition) {\n    \u002F\u002F savedPosition: browser back\u002Fforward → restore previous scroll\n    if (savedPosition) return savedPosition\n    \u002F\u002F Hash anchor: scroll to the element\n    if (to.hash) return { el: to.hash, behavior: 'smooth' }\n    \u002F\u002F Default: top of page\n    return { top: 0, behavior: 'smooth' }\n  },\n})\n","",[31,32,33,59,66,73,79,85,90,116,122,134,145,151,175,181,186,196,206,212,233,239,257,263,269,275,290,301,320,326,331,341,351,369,374,380,385,390,396,406,416,434,440,450,455,460,466,476,486,504,509,515,520,541,568,574,580,605,611,626,632,651,657,676,681],"code",{"__ignoreMap":29},[34,35,38,42,46,49,52,55],"span",{"class":36,"line":37},"line",1,[34,39,41],{"class":40},"svdQ7","import",[34,43,45],{"class":44},"ssxIu"," { createRouter, createWebHistory, ",[34,47,48],{"class":40},"type",[34,50,51],{"class":44}," RouteRecordRaw } ",[34,53,54],{"class":40},"from",[34,56,58],{"class":57},"sJ6F3"," 'vue-router'\n",[34,60,62],{"class":36,"line":61},2,[34,63,65],{"emptyLinePlaceholder":64},true,"\n",[34,67,69],{"class":36,"line":68},3,[34,70,72],{"class":71},"sdCPZ","\u002F\u002F ── Dynamic import = code splitting ────────────────────\n",[34,74,76],{"class":36,"line":75},4,[34,77,78],{"class":71},"\u002F\u002F Each route's component is a SEPARATE chunk, loaded on first navigation.\n",[34,80,82],{"class":36,"line":81},5,[34,83,84],{"class":71},"\u002F\u002F Reduces initial bundle size — only the visited route's code downloads.\n",[34,86,88],{"class":36,"line":87},6,[34,89,65],{"emptyLinePlaceholder":64},[34,91,93,96,100,103,107,110,113],{"class":36,"line":92},7,[34,94,95],{"class":40},"const",[34,97,99],{"class":98},"snvgF"," routes",[34,101,102],{"class":40},":",[34,104,106],{"class":105},"sIsaT"," RouteRecordRaw",[34,108,109],{"class":44},"[] ",[34,111,112],{"class":40},"=",[34,114,115],{"class":44}," [\n",[34,117,119],{"class":36,"line":118},8,[34,120,121],{"class":44},"  {\n",[34,123,125,128,131],{"class":36,"line":124},9,[34,126,127],{"class":44},"    path: ",[34,129,130],{"class":57},"'\u002F'",[34,132,133],{"class":44},",\n",[34,135,137,140,143],{"class":36,"line":136},10,[34,138,139],{"class":44},"    name: ",[34,141,142],{"class":57},"'home'",[34,144,133],{"class":44},[34,146,148],{"class":36,"line":147},11,[34,149,150],{"class":71},"    \u002F\u002F Eager import: part of the main bundle (critical for above-the-fold)\n",[34,152,154,157,160,163,166,169,172],{"class":36,"line":153},12,[34,155,156],{"class":105},"    component",[34,158,159],{"class":44},": () ",[34,161,162],{"class":40},"=>",[34,164,165],{"class":40}," import",[34,167,168],{"class":44},"(",[34,170,171],{"class":57},"'@\u002Fviews\u002FHomeView.vue'",[34,173,174],{"class":44},"),\n",[34,176,178],{"class":36,"line":177},13,[34,179,180],{"class":44},"  },\n",[34,182,184],{"class":36,"line":183},14,[34,185,121],{"class":44},[34,187,189,191,194],{"class":36,"line":188},15,[34,190,127],{"class":44},[34,192,193],{"class":57},"'\u002Fdashboard'",[34,195,133],{"class":44},[34,197,199,201,204],{"class":36,"line":198},16,[34,200,139],{"class":44},[34,202,203],{"class":57},"'dashboard'",[34,205,133],{"class":44},[34,207,209],{"class":36,"line":208},17,[34,210,211],{"class":71},"    \u002F\u002F ── Chunk name via magic comment (for bundle analysis) ──\n",[34,213,215,217,219,221,223,225,228,231],{"class":36,"line":214},18,[34,216,156],{"class":105},[34,218,159],{"class":44},[34,220,162],{"class":40},[34,222,165],{"class":105},[34,224,168],{"class":44},[34,226,227],{"class":71},"\u002F* webpackChunkName: \"dashboard\" *\u002F",[34,229,230],{"class":57}," '@\u002Fviews\u002FDashboardView.vue'",[34,232,174],{"class":44},[34,234,236],{"class":36,"line":235},19,[34,237,238],{"class":71},"    \u002F\u002F ── Meta: route-level metadata accessible in guards ──\n",[34,240,242,245,248,251,254],{"class":36,"line":241},20,[34,243,244],{"class":44},"    meta: { requiresAuth: ",[34,246,247],{"class":98},"true",[34,249,250],{"class":44},", title: ",[34,252,253],{"class":57},"'Dashboard'",[34,255,256],{"class":44}," },\n",[34,258,260],{"class":36,"line":259},21,[34,261,262],{"class":71},"    \u002F\u002F ── Nested routes: render in \u003CRouterView> inside the parent ──\n",[34,264,266],{"class":36,"line":265},22,[34,267,268],{"class":44},"    children: [\n",[34,270,272],{"class":36,"line":271},23,[34,273,274],{"class":44},"      {\n",[34,276,278,281,284,287],{"class":36,"line":277},24,[34,279,280],{"class":44},"        path: ",[34,282,283],{"class":57},"'settings'",[34,285,286],{"class":44},",  ",[34,288,289],{"class":71},"\u002F\u002F matches \u002Fdashboard\u002Fsettings (no leading slash)\n",[34,291,293,296,299],{"class":36,"line":292},25,[34,294,295],{"class":44},"        name: ",[34,297,298],{"class":57},"'dashboard.settings'",[34,300,133],{"class":44},[34,302,304,307,309,311,313,315,318],{"class":36,"line":303},26,[34,305,306],{"class":105},"        component",[34,308,159],{"class":44},[34,310,162],{"class":40},[34,312,165],{"class":40},[34,314,168],{"class":44},[34,316,317],{"class":57},"'@\u002Fviews\u002FSettingsView.vue'",[34,319,174],{"class":44},[34,321,323],{"class":36,"line":322},27,[34,324,325],{"class":44},"      },\n",[34,327,329],{"class":36,"line":328},28,[34,330,274],{"class":44},[34,332,334,336,339],{"class":36,"line":333},29,[34,335,280],{"class":44},[34,337,338],{"class":57},"'analytics'",[34,340,133],{"class":44},[34,342,344,346,349],{"class":36,"line":343},30,[34,345,295],{"class":44},[34,347,348],{"class":57},"'dashboard.analytics'",[34,350,133],{"class":44},[34,352,354,356,358,360,362,364,367],{"class":36,"line":353},31,[34,355,306],{"class":105},[34,357,159],{"class":44},[34,359,162],{"class":40},[34,361,165],{"class":40},[34,363,168],{"class":44},[34,365,366],{"class":57},"'@\u002Fviews\u002FAnalyticsView.vue'",[34,368,174],{"class":44},[34,370,372],{"class":36,"line":371},32,[34,373,325],{"class":44},[34,375,377],{"class":36,"line":376},33,[34,378,379],{"class":44},"    ],\n",[34,381,383],{"class":36,"line":382},34,[34,384,180],{"class":44},[34,386,388],{"class":36,"line":387},35,[34,389,121],{"class":44},[34,391,393],{"class":36,"line":392},36,[34,394,395],{"class":71},"    \u002F\u002F ── Dynamic route params: \u002Fusers\u002F42 → params.id = \"42\" (string!) ──\n",[34,397,399,401,404],{"class":36,"line":398},37,[34,400,127],{"class":44},[34,402,403],{"class":57},"'\u002Fusers\u002F:id'",[34,405,133],{"class":44},[34,407,409,411,414],{"class":36,"line":408},38,[34,410,139],{"class":44},[34,412,413],{"class":57},"'user'",[34,415,133],{"class":44},[34,417,419,421,423,425,427,429,432],{"class":36,"line":418},39,[34,420,156],{"class":105},[34,422,159],{"class":44},[34,424,162],{"class":40},[34,426,165],{"class":40},[34,428,168],{"class":44},[34,430,431],{"class":57},"'@\u002Fviews\u002FUserView.vue'",[34,433,174],{"class":44},[34,435,437],{"class":36,"line":436},40,[34,438,439],{"class":71},"    \u002F\u002F Props mode: pass route params as component props (decouples from $route)\n",[34,441,443,446,448],{"class":36,"line":442},41,[34,444,445],{"class":44},"    props: ",[34,447,247],{"class":98},[34,449,133],{"class":44},[34,451,453],{"class":36,"line":452},42,[34,454,180],{"class":44},[34,456,458],{"class":36,"line":457},43,[34,459,121],{"class":44},[34,461,463],{"class":36,"line":462},44,[34,464,465],{"class":71},"    \u002F\u002F ── Catch-all 404: must be LAST in the routes array ──\n",[34,467,469,471,474],{"class":36,"line":468},45,[34,470,127],{"class":44},[34,472,473],{"class":57},"'\u002F:pathMatch(.*)*'",[34,475,133],{"class":44},[34,477,479,481,484],{"class":36,"line":478},46,[34,480,139],{"class":44},[34,482,483],{"class":57},"'not-found'",[34,485,133],{"class":44},[34,487,489,491,493,495,497,499,502],{"class":36,"line":488},47,[34,490,156],{"class":105},[34,492,159],{"class":44},[34,494,162],{"class":40},[34,496,165],{"class":40},[34,498,168],{"class":44},[34,500,501],{"class":57},"'@\u002Fviews\u002FNotFoundView.vue'",[34,503,174],{"class":44},[34,505,507],{"class":36,"line":506},48,[34,508,180],{"class":44},[34,510,512],{"class":36,"line":511},49,[34,513,514],{"class":44},"]\n",[34,516,518],{"class":36,"line":517},50,[34,519,65],{"emptyLinePlaceholder":64},[34,521,523,526,529,532,535,538],{"class":36,"line":522},51,[34,524,525],{"class":40},"export",[34,527,528],{"class":40}," const",[34,530,531],{"class":98}," router",[34,533,534],{"class":40}," =",[34,536,537],{"class":105}," createRouter",[34,539,540],{"class":44},"({\n",[34,542,544,547,550,552,554,557,560,563,566],{"class":36,"line":543},52,[34,545,546],{"class":44},"  history: ",[34,548,549],{"class":105},"createWebHistory",[34,551,168],{"class":44},[34,553,41],{"class":40},[34,555,556],{"class":44},".",[34,558,559],{"class":98},"meta",[34,561,562],{"class":44},".env.",[34,564,565],{"class":98},"BASE_URL",[34,567,174],{"class":44},[34,569,571],{"class":36,"line":570},53,[34,572,573],{"class":44},"  routes,\n",[34,575,577],{"class":36,"line":576},54,[34,578,579],{"class":71},"  \u002F\u002F ── scrollBehavior: control scroll on navigation ──\n",[34,581,583,586,588,592,595,597,599,602],{"class":36,"line":582},55,[34,584,585],{"class":105},"  scrollBehavior",[34,587,168],{"class":44},[34,589,591],{"class":590},"sCrzJ","to",[34,593,594],{"class":44},", ",[34,596,54],{"class":590},[34,598,594],{"class":44},[34,600,601],{"class":590},"savedPosition",[34,603,604],{"class":44},") {\n",[34,606,608],{"class":36,"line":607},56,[34,609,610],{"class":71},"    \u002F\u002F savedPosition: browser back\u002Fforward → restore previous scroll\n",[34,612,614,617,620,623],{"class":36,"line":613},57,[34,615,616],{"class":40},"    if",[34,618,619],{"class":44}," (savedPosition) ",[34,621,622],{"class":40},"return",[34,624,625],{"class":44}," savedPosition\n",[34,627,629],{"class":36,"line":628},58,[34,630,631],{"class":71},"    \u002F\u002F Hash anchor: scroll to the element\n",[34,633,635,637,640,642,645,648],{"class":36,"line":634},59,[34,636,616],{"class":40},[34,638,639],{"class":44}," (to.hash) ",[34,641,622],{"class":40},[34,643,644],{"class":44}," { el: to.hash, behavior: ",[34,646,647],{"class":57},"'smooth'",[34,649,650],{"class":44}," }\n",[34,652,654],{"class":36,"line":653},60,[34,655,656],{"class":71},"    \u002F\u002F Default: top of page\n",[34,658,660,663,666,669,672,674],{"class":36,"line":659},61,[34,661,662],{"class":40},"    return",[34,664,665],{"class":44}," { top: ",[34,667,668],{"class":98},"0",[34,670,671],{"class":44},", behavior: ",[34,673,647],{"class":57},[34,675,650],{"class":44},[34,677,679],{"class":36,"line":678},62,[34,680,180],{"class":44},[34,682,684],{"class":36,"line":683},63,[34,685,686],{"class":44},"})\n",[15,688,690],{"id":689},"navigation-guards-composable-auth-pipeline","Navigation Guards — Composable Auth Pipeline",[20,692,694],{"filename":693,"language":23},"router\u002Fguards.ts",[25,695,697],{"className":27,"code":696,"language":23,"meta":29,"style":29},"import { type Router } from 'vue-router'\nimport { useUserStore } from '@\u002Fstores\u002Fuser'\n\n\u002F\u002F ── Global before guard: runs before EVERY navigation ──\n\u002F\u002F `to`: target route, `from`: current route, `next`: control function\n\u002F\u002F In Vue Router 4, return values replace next():\n\u002F\u002F   return false → cancel navigation\n\u002F\u002F   return '\u002Fpath' → redirect\n\u002F\u002F   return { name: 'route' } → redirect by name\n\u002F\u002F   return undefined\u002Ftrue → proceed\nexport function setupGuards(router: Router) {\n  router.beforeEach(async (to, from) => {\n    const userStore = useUserStore()\n\n    \u002F\u002F ── Check meta.requiresAuth on the route or any parent ──\n    \u002F\u002F to.matched: array of matched route records (parent → child)\n    \u002F\u002F .some() checks if ANY matched route requires auth\n    const requiresAuth = to.matched.some(r => r.meta.requiresAuth)\n\n    if (requiresAuth && !userStore.isAuthenticated) {\n      \u002F\u002F ── Redirect to login, preserve the intended destination ──\n      return {\n        name: 'login',\n        query: { redirect: to.fullPath },  \u002F\u002F return to original page after login\n      }\n    }\n\n    \u002F\u002F ── If already authenticated and going to login, redirect to dashboard ──\n    if (to.name === 'login' && userStore.isAuthenticated) {\n      return { name: 'dashboard' }\n    }\n\n    \u002F\u002F ── Set document title from route meta ──\n    if (to.meta.title) {\n      document.title = `${to.meta.title} — MyApp`\n    }\n\n    \u002F\u002F No return → proceed with navigation\n  })\n\n  \u002F\u002F ── Per-route guard: runs only for this specific route ──\n  \u002F\u002F More efficient than global guard for route-specific checks\n  router.beforeResolve(async (to) => {\n    \u002F\u002F Runs after all beforeEach guards resolve, before component loading\n    \u002F\u002F Good for: fetching data that multiple components need\n    if (to.meta.requiresFeature) {\n      const featureStore = useFeatureStore()\n      await featureStore.checkFeature(to.meta.requiresFeature as string)\n      if (!featureStore.hasFeature) {\n        return { name: 'upgrade' }\n      }\n    }\n  })\n\n  \u002F\u002F ── afterEach: runs after navigation is confirmed ──\n  \u002F\u002F Cannot cancel navigation — use for analytics, logging\n  router.afterEach((to, from, failure) => {\n    if (!failure) {\n      analytics.track('page_view', { path: to.fullPath })\n    }\n  })\n}\n",[31,698,699,715,727,731,736,741,746,751,756,761,766,788,818,834,838,843,848,853,879,883,899,904,911,920,928,933,938,942,947,966,977,981,985,990,997,1021,1025,1029,1034,1039,1043,1048,1053,1074,1079,1084,1091,1106,1129,1142,1154,1158,1162,1166,1170,1175,1180,1207,1218,1234,1238,1242],{"__ignoreMap":29},[34,700,701,703,706,708,711,713],{"class":36,"line":37},[34,702,41],{"class":40},[34,704,705],{"class":44}," { ",[34,707,48],{"class":40},[34,709,710],{"class":44}," Router } ",[34,712,54],{"class":40},[34,714,58],{"class":57},[34,716,717,719,722,724],{"class":36,"line":61},[34,718,41],{"class":40},[34,720,721],{"class":44}," { useUserStore } ",[34,723,54],{"class":40},[34,725,726],{"class":57}," '@\u002Fstores\u002Fuser'\n",[34,728,729],{"class":36,"line":68},[34,730,65],{"emptyLinePlaceholder":64},[34,732,733],{"class":36,"line":75},[34,734,735],{"class":71},"\u002F\u002F ── Global before guard: runs before EVERY navigation ──\n",[34,737,738],{"class":36,"line":81},[34,739,740],{"class":71},"\u002F\u002F `to`: target route, `from`: current route, `next`: control function\n",[34,742,743],{"class":36,"line":87},[34,744,745],{"class":71},"\u002F\u002F In Vue Router 4, return values replace next():\n",[34,747,748],{"class":36,"line":92},[34,749,750],{"class":71},"\u002F\u002F   return false → cancel navigation\n",[34,752,753],{"class":36,"line":118},[34,754,755],{"class":71},"\u002F\u002F   return '\u002Fpath' → redirect\n",[34,757,758],{"class":36,"line":124},[34,759,760],{"class":71},"\u002F\u002F   return { name: 'route' } → redirect by name\n",[34,762,763],{"class":36,"line":136},[34,764,765],{"class":71},"\u002F\u002F   return undefined\u002Ftrue → proceed\n",[34,767,768,770,773,776,778,781,783,786],{"class":36,"line":147},[34,769,525],{"class":40},[34,771,772],{"class":40}," function",[34,774,775],{"class":105}," setupGuards",[34,777,168],{"class":44},[34,779,780],{"class":590},"router",[34,782,102],{"class":40},[34,784,785],{"class":105}," Router",[34,787,604],{"class":44},[34,789,790,793,796,798,801,804,806,808,810,813,815],{"class":36,"line":153},[34,791,792],{"class":44},"  router.",[34,794,795],{"class":105},"beforeEach",[34,797,168],{"class":44},[34,799,800],{"class":40},"async",[34,802,803],{"class":44}," (",[34,805,591],{"class":590},[34,807,594],{"class":44},[34,809,54],{"class":590},[34,811,812],{"class":44},") ",[34,814,162],{"class":40},[34,816,817],{"class":44}," {\n",[34,819,820,823,826,828,831],{"class":36,"line":177},[34,821,822],{"class":40},"    const",[34,824,825],{"class":98}," userStore",[34,827,534],{"class":40},[34,829,830],{"class":105}," useUserStore",[34,832,833],{"class":44},"()\n",[34,835,836],{"class":36,"line":183},[34,837,65],{"emptyLinePlaceholder":64},[34,839,840],{"class":36,"line":188},[34,841,842],{"class":71},"    \u002F\u002F ── Check meta.requiresAuth on the route or any parent ──\n",[34,844,845],{"class":36,"line":198},[34,846,847],{"class":71},"    \u002F\u002F to.matched: array of matched route records (parent → child)\n",[34,849,850],{"class":36,"line":208},[34,851,852],{"class":71},"    \u002F\u002F .some() checks if ANY matched route requires auth\n",[34,854,855,857,860,862,865,868,870,873,876],{"class":36,"line":214},[34,856,822],{"class":40},[34,858,859],{"class":98}," requiresAuth",[34,861,534],{"class":40},[34,863,864],{"class":44}," to.matched.",[34,866,867],{"class":105},"some",[34,869,168],{"class":44},[34,871,872],{"class":590},"r",[34,874,875],{"class":40}," =>",[34,877,878],{"class":44}," r.meta.requiresAuth)\n",[34,880,881],{"class":36,"line":235},[34,882,65],{"emptyLinePlaceholder":64},[34,884,885,887,890,893,896],{"class":36,"line":241},[34,886,616],{"class":40},[34,888,889],{"class":44}," (requiresAuth ",[34,891,892],{"class":40},"&&",[34,894,895],{"class":40}," !",[34,897,898],{"class":44},"userStore.isAuthenticated) {\n",[34,900,901],{"class":36,"line":259},[34,902,903],{"class":71},"      \u002F\u002F ── Redirect to login, preserve the intended destination ──\n",[34,905,906,909],{"class":36,"line":265},[34,907,908],{"class":40},"      return",[34,910,817],{"class":44},[34,912,913,915,918],{"class":36,"line":271},[34,914,295],{"class":44},[34,916,917],{"class":57},"'login'",[34,919,133],{"class":44},[34,921,922,925],{"class":36,"line":277},[34,923,924],{"class":44},"        query: { redirect: to.fullPath },  ",[34,926,927],{"class":71},"\u002F\u002F return to original page after login\n",[34,929,930],{"class":36,"line":292},[34,931,932],{"class":44},"      }\n",[34,934,935],{"class":36,"line":303},[34,936,937],{"class":44},"    }\n",[34,939,940],{"class":36,"line":322},[34,941,65],{"emptyLinePlaceholder":64},[34,943,944],{"class":36,"line":328},[34,945,946],{"class":71},"    \u002F\u002F ── If already authenticated and going to login, redirect to dashboard ──\n",[34,948,949,951,954,957,960,963],{"class":36,"line":333},[34,950,616],{"class":40},[34,952,953],{"class":44}," (to.name ",[34,955,956],{"class":40},"===",[34,958,959],{"class":57}," 'login'",[34,961,962],{"class":40}," &&",[34,964,965],{"class":44}," userStore.isAuthenticated) {\n",[34,967,968,970,973,975],{"class":36,"line":343},[34,969,908],{"class":40},[34,971,972],{"class":44}," { name: ",[34,974,203],{"class":57},[34,976,650],{"class":44},[34,978,979],{"class":36,"line":353},[34,980,937],{"class":44},[34,982,983],{"class":36,"line":371},[34,984,65],{"emptyLinePlaceholder":64},[34,986,987],{"class":36,"line":376},[34,988,989],{"class":71},"    \u002F\u002F ── Set document title from route meta ──\n",[34,991,992,994],{"class":36,"line":382},[34,993,616],{"class":40},[34,995,996],{"class":44}," (to.meta.title) {\n",[34,998,999,1002,1004,1007,1009,1011,1013,1015,1018],{"class":36,"line":387},[34,1000,1001],{"class":44},"      document.title ",[34,1003,112],{"class":40},[34,1005,1006],{"class":57}," `${",[34,1008,591],{"class":44},[34,1010,556],{"class":57},[34,1012,559],{"class":44},[34,1014,556],{"class":57},[34,1016,1017],{"class":44},"title",[34,1019,1020],{"class":57},"} — MyApp`\n",[34,1022,1023],{"class":36,"line":392},[34,1024,937],{"class":44},[34,1026,1027],{"class":36,"line":398},[34,1028,65],{"emptyLinePlaceholder":64},[34,1030,1031],{"class":36,"line":408},[34,1032,1033],{"class":71},"    \u002F\u002F No return → proceed with navigation\n",[34,1035,1036],{"class":36,"line":418},[34,1037,1038],{"class":44},"  })\n",[34,1040,1041],{"class":36,"line":436},[34,1042,65],{"emptyLinePlaceholder":64},[34,1044,1045],{"class":36,"line":442},[34,1046,1047],{"class":71},"  \u002F\u002F ── Per-route guard: runs only for this specific route ──\n",[34,1049,1050],{"class":36,"line":452},[34,1051,1052],{"class":71},"  \u002F\u002F More efficient than global guard for route-specific checks\n",[34,1054,1055,1057,1060,1062,1064,1066,1068,1070,1072],{"class":36,"line":457},[34,1056,792],{"class":44},[34,1058,1059],{"class":105},"beforeResolve",[34,1061,168],{"class":44},[34,1063,800],{"class":40},[34,1065,803],{"class":44},[34,1067,591],{"class":590},[34,1069,812],{"class":44},[34,1071,162],{"class":40},[34,1073,817],{"class":44},[34,1075,1076],{"class":36,"line":462},[34,1077,1078],{"class":71},"    \u002F\u002F Runs after all beforeEach guards resolve, before component loading\n",[34,1080,1081],{"class":36,"line":468},[34,1082,1083],{"class":71},"    \u002F\u002F Good for: fetching data that multiple components need\n",[34,1085,1086,1088],{"class":36,"line":478},[34,1087,616],{"class":40},[34,1089,1090],{"class":44}," (to.meta.requiresFeature) {\n",[34,1092,1093,1096,1099,1101,1104],{"class":36,"line":488},[34,1094,1095],{"class":40},"      const",[34,1097,1098],{"class":98}," featureStore",[34,1100,534],{"class":40},[34,1102,1103],{"class":105}," useFeatureStore",[34,1105,833],{"class":44},[34,1107,1108,1111,1114,1117,1120,1123,1126],{"class":36,"line":506},[34,1109,1110],{"class":40},"      await",[34,1112,1113],{"class":44}," featureStore.",[34,1115,1116],{"class":105},"checkFeature",[34,1118,1119],{"class":44},"(to.meta.requiresFeature ",[34,1121,1122],{"class":40},"as",[34,1124,1125],{"class":98}," string",[34,1127,1128],{"class":44},")\n",[34,1130,1131,1134,1136,1139],{"class":36,"line":511},[34,1132,1133],{"class":40},"      if",[34,1135,803],{"class":44},[34,1137,1138],{"class":40},"!",[34,1140,1141],{"class":44},"featureStore.hasFeature) {\n",[34,1143,1144,1147,1149,1152],{"class":36,"line":517},[34,1145,1146],{"class":40},"        return",[34,1148,972],{"class":44},[34,1150,1151],{"class":57},"'upgrade'",[34,1153,650],{"class":44},[34,1155,1156],{"class":36,"line":522},[34,1157,932],{"class":44},[34,1159,1160],{"class":36,"line":543},[34,1161,937],{"class":44},[34,1163,1164],{"class":36,"line":570},[34,1165,1038],{"class":44},[34,1167,1168],{"class":36,"line":576},[34,1169,65],{"emptyLinePlaceholder":64},[34,1171,1172],{"class":36,"line":582},[34,1173,1174],{"class":71},"  \u002F\u002F ── afterEach: runs after navigation is confirmed ──\n",[34,1176,1177],{"class":36,"line":607},[34,1178,1179],{"class":71},"  \u002F\u002F Cannot cancel navigation — use for analytics, logging\n",[34,1181,1182,1184,1187,1190,1192,1194,1196,1198,1201,1203,1205],{"class":36,"line":613},[34,1183,792],{"class":44},[34,1185,1186],{"class":105},"afterEach",[34,1188,1189],{"class":44},"((",[34,1191,591],{"class":590},[34,1193,594],{"class":44},[34,1195,54],{"class":590},[34,1197,594],{"class":44},[34,1199,1200],{"class":590},"failure",[34,1202,812],{"class":44},[34,1204,162],{"class":40},[34,1206,817],{"class":44},[34,1208,1209,1211,1213,1215],{"class":36,"line":628},[34,1210,616],{"class":40},[34,1212,803],{"class":44},[34,1214,1138],{"class":40},[34,1216,1217],{"class":44},"failure) {\n",[34,1219,1220,1223,1226,1228,1231],{"class":36,"line":634},[34,1221,1222],{"class":44},"      analytics.",[34,1224,1225],{"class":105},"track",[34,1227,168],{"class":44},[34,1229,1230],{"class":57},"'page_view'",[34,1232,1233],{"class":44},", { path: to.fullPath })\n",[34,1235,1236],{"class":36,"line":653},[34,1237,937],{"class":44},[34,1239,1240],{"class":36,"line":659},[34,1241,1038],{"class":44},[34,1243,1244],{"class":36,"line":678},[34,1245,1246],{"class":44},"}\n",[15,1248,1250],{"id":1249},"in-component-guards-beforerouteupdate-vs-watch-route","In-Component Guards — beforeRouteUpdate vs watch $route",[20,1252,1255],{"filename":1253,"language":1254},"UserView.vue","vue",[25,1256,1259],{"className":1257,"code":1258,"language":1254,"meta":29,"style":29},"language-vue shiki shiki-themes github-light github-dark","\u003Cscript setup>\nimport { watch, onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'\nimport { ref } from 'vue'\n\nconst route = useRoute()   \u002F\u002F current route (reactive)\nconst router = useRouter() \u002F\u002F navigation methods\nconst user = ref(null)\n\nasync function fetchUser(id) {\n  const res = await fetch(`\u002Fapi\u002Fusers\u002F${id}`)\n  user.value = await res.json()\n}\n\n\u002F\u002F ── onBeforeRouteUpdate: fires when navigating between same-route params ──\n\u002F\u002F \u002Fusers\u002F1 → \u002Fusers\u002F2: same component, different param. Component is REUSED.\n\u002F\u002F This guard fires, giving you a chance to refetch data.\nonBeforeRouteUpdate(async (to, from) => {\n  \u002F\u002F `to.params.id` is the NEW param; route.params is still the old one\n  if (to.params.id !== from.params.id) {\n    await fetchUser(to.params.id)\n  }\n  \u002F\u002F return false to cancel the navigation\n})\n\n\u002F\u002F ── Alternative: watch the route param reactively ──\nwatch(\n  () => route.params.id,\n  async (newId) => {\n    if (newId) await fetchUser(newId)\n  },\n  { immediate: true }\n)\n\u002F\u002F Both work — onBeforeRouteUpdate is more explicit and can cancel navigation.\n\u003C\u002Fscript>\n",[31,1260,1261,1276,1287,1299,1303,1321,1338,1357,1361,1377,1405,1422,1426,1430,1435,1440,1445,1468,1473,1487,1497,1502,1507,1511,1515,1520,1528,1538,1554,1569,1573,1582,1586,1591],{"__ignoreMap":29},[34,1262,1263,1266,1270,1273],{"class":36,"line":37},[34,1264,1265],{"class":44},"\u003C",[34,1267,1269],{"class":1268},"sk71V","script",[34,1271,1272],{"class":105}," setup",[34,1274,1275],{"class":44},">\n",[34,1277,1278,1280,1283,1285],{"class":36,"line":61},[34,1279,41],{"class":40},[34,1281,1282],{"class":44}," { watch, onBeforeRouteUpdate, useRoute, useRouter } ",[34,1284,54],{"class":40},[34,1286,58],{"class":57},[34,1288,1289,1291,1294,1296],{"class":36,"line":68},[34,1290,41],{"class":40},[34,1292,1293],{"class":44}," { ref } ",[34,1295,54],{"class":40},[34,1297,1298],{"class":57}," 'vue'\n",[34,1300,1301],{"class":36,"line":75},[34,1302,65],{"emptyLinePlaceholder":64},[34,1304,1305,1307,1310,1312,1315,1318],{"class":36,"line":81},[34,1306,95],{"class":40},[34,1308,1309],{"class":98}," route",[34,1311,534],{"class":40},[34,1313,1314],{"class":105}," useRoute",[34,1316,1317],{"class":44},"()   ",[34,1319,1320],{"class":71},"\u002F\u002F current route (reactive)\n",[34,1322,1323,1325,1327,1329,1332,1335],{"class":36,"line":87},[34,1324,95],{"class":40},[34,1326,531],{"class":98},[34,1328,534],{"class":40},[34,1330,1331],{"class":105}," useRouter",[34,1333,1334],{"class":44},"() ",[34,1336,1337],{"class":71},"\u002F\u002F navigation methods\n",[34,1339,1340,1342,1345,1347,1350,1352,1355],{"class":36,"line":92},[34,1341,95],{"class":40},[34,1343,1344],{"class":98}," user",[34,1346,534],{"class":40},[34,1348,1349],{"class":105}," ref",[34,1351,168],{"class":44},[34,1353,1354],{"class":98},"null",[34,1356,1128],{"class":44},[34,1358,1359],{"class":36,"line":118},[34,1360,65],{"emptyLinePlaceholder":64},[34,1362,1363,1365,1367,1370,1372,1375],{"class":36,"line":124},[34,1364,800],{"class":40},[34,1366,772],{"class":40},[34,1368,1369],{"class":105}," fetchUser",[34,1371,168],{"class":44},[34,1373,1374],{"class":590},"id",[34,1376,604],{"class":44},[34,1378,1379,1382,1385,1387,1390,1393,1395,1398,1400,1403],{"class":36,"line":136},[34,1380,1381],{"class":40},"  const",[34,1383,1384],{"class":98}," res",[34,1386,534],{"class":40},[34,1388,1389],{"class":40}," await",[34,1391,1392],{"class":105}," fetch",[34,1394,168],{"class":44},[34,1396,1397],{"class":57},"`\u002Fapi\u002Fusers\u002F${",[34,1399,1374],{"class":44},[34,1401,1402],{"class":57},"}`",[34,1404,1128],{"class":44},[34,1406,1407,1410,1412,1414,1417,1420],{"class":36,"line":147},[34,1408,1409],{"class":44},"  user.value ",[34,1411,112],{"class":40},[34,1413,1389],{"class":40},[34,1415,1416],{"class":44}," res.",[34,1418,1419],{"class":105},"json",[34,1421,833],{"class":44},[34,1423,1424],{"class":36,"line":153},[34,1425,1246],{"class":44},[34,1427,1428],{"class":36,"line":177},[34,1429,65],{"emptyLinePlaceholder":64},[34,1431,1432],{"class":36,"line":183},[34,1433,1434],{"class":71},"\u002F\u002F ── onBeforeRouteUpdate: fires when navigating between same-route params ──\n",[34,1436,1437],{"class":36,"line":188},[34,1438,1439],{"class":71},"\u002F\u002F \u002Fusers\u002F1 → \u002Fusers\u002F2: same component, different param. Component is REUSED.\n",[34,1441,1442],{"class":36,"line":198},[34,1443,1444],{"class":71},"\u002F\u002F This guard fires, giving you a chance to refetch data.\n",[34,1446,1447,1450,1452,1454,1456,1458,1460,1462,1464,1466],{"class":36,"line":208},[34,1448,1449],{"class":105},"onBeforeRouteUpdate",[34,1451,168],{"class":44},[34,1453,800],{"class":40},[34,1455,803],{"class":44},[34,1457,591],{"class":590},[34,1459,594],{"class":44},[34,1461,54],{"class":590},[34,1463,812],{"class":44},[34,1465,162],{"class":40},[34,1467,817],{"class":44},[34,1469,1470],{"class":36,"line":214},[34,1471,1472],{"class":71},"  \u002F\u002F `to.params.id` is the NEW param; route.params is still the old one\n",[34,1474,1475,1478,1481,1484],{"class":36,"line":235},[34,1476,1477],{"class":40},"  if",[34,1479,1480],{"class":44}," (to.params.id ",[34,1482,1483],{"class":40},"!==",[34,1485,1486],{"class":44}," from.params.id) {\n",[34,1488,1489,1492,1494],{"class":36,"line":241},[34,1490,1491],{"class":40},"    await",[34,1493,1369],{"class":105},[34,1495,1496],{"class":44},"(to.params.id)\n",[34,1498,1499],{"class":36,"line":259},[34,1500,1501],{"class":44},"  }\n",[34,1503,1504],{"class":36,"line":265},[34,1505,1506],{"class":71},"  \u002F\u002F return false to cancel the navigation\n",[34,1508,1509],{"class":36,"line":271},[34,1510,686],{"class":44},[34,1512,1513],{"class":36,"line":277},[34,1514,65],{"emptyLinePlaceholder":64},[34,1516,1517],{"class":36,"line":292},[34,1518,1519],{"class":71},"\u002F\u002F ── Alternative: watch the route param reactively ──\n",[34,1521,1522,1525],{"class":36,"line":303},[34,1523,1524],{"class":105},"watch",[34,1526,1527],{"class":44},"(\n",[34,1529,1530,1533,1535],{"class":36,"line":322},[34,1531,1532],{"class":44},"  () ",[34,1534,162],{"class":40},[34,1536,1537],{"class":44}," route.params.id,\n",[34,1539,1540,1543,1545,1548,1550,1552],{"class":36,"line":328},[34,1541,1542],{"class":40},"  async",[34,1544,803],{"class":44},[34,1546,1547],{"class":590},"newId",[34,1549,812],{"class":44},[34,1551,162],{"class":40},[34,1553,817],{"class":44},[34,1555,1556,1558,1561,1564,1566],{"class":36,"line":333},[34,1557,616],{"class":40},[34,1559,1560],{"class":44}," (newId) ",[34,1562,1563],{"class":40},"await",[34,1565,1369],{"class":105},[34,1567,1568],{"class":44},"(newId)\n",[34,1570,1571],{"class":36,"line":343},[34,1572,180],{"class":44},[34,1574,1575,1578,1580],{"class":36,"line":353},[34,1576,1577],{"class":44},"  { immediate: ",[34,1579,247],{"class":98},[34,1581,650],{"class":44},[34,1583,1584],{"class":36,"line":371},[34,1585,1128],{"class":44},[34,1587,1588],{"class":36,"line":376},[34,1589,1590],{"class":71},"\u002F\u002F Both work — onBeforeRouteUpdate is more explicit and can cancel navigation.\n",[34,1592,1593,1596,1598],{"class":36,"line":382},[34,1594,1595],{"class":44},"\u003C\u002F",[34,1597,1269],{"class":1268},[34,1599,1275],{"class":44},[15,1601,1603],{"id":1602},"programmatic-navigation","Programmatic Navigation",[20,1605,1607],{"filename":1606,"language":23},"navigation.ts",[25,1608,1610],{"className":27,"code":1609,"language":23,"meta":29,"style":29},"import { useRouter, useRoute } from 'vue-router'\n\nconst router = useRouter()\nconst route = useRoute()\n\n\u002F\u002F ── Push: add to history stack (back button returns) ──\nrouter.push('\u002Fusers\u002F42')\nrouter.push({ name: 'user', params: { id: '42' } })  \u002F\u002F by name (preferred)\nrouter.push({ path: '\u002Fsearch', query: { q: 'vue', page: '2' } })\n\n\u002F\u002F ── Replace: replace current entry (back button skips this page) ──\n\u002F\u002F Use after login: don't want back button to return to the login page\nrouter.replace({ name: 'dashboard' })\n\n\u002F\u002F ── Query params: always strings — type coercion needed ──\nconst page = parseInt(route.query.page as string) || 1\nconst q = route.query.q as string\n\n\u002F\u002F ── Dynamic params: always strings — even if defined as \u002Fusers\u002F:id ──\nconst userId = route.params.id  \u002F\u002F \"42\" (string), NOT 42 (number)\n\n\u002F\u002F ── Go: relative history movement ──\nrouter.go(-1)  \u002F\u002F back\nrouter.go(1)   \u002F\u002F forward\nrouter.go(-2)  \u002F\u002F back two pages\n\n\u002F\u002F ── Async navigation: returns a promise ──\nawait router.push('\u002Fdashboard')  \u002F\u002F resolves after navigation completes\n",[31,1611,1612,1623,1627,1639,1651,1655,1660,1675,1698,1725,1729,1734,1739,1753,1757,1762,1789,1806,1810,1815,1830,1834,1839,1860,1876,1894,1898,1903],{"__ignoreMap":29},[34,1613,1614,1616,1619,1621],{"class":36,"line":37},[34,1615,41],{"class":40},[34,1617,1618],{"class":44}," { useRouter, useRoute } ",[34,1620,54],{"class":40},[34,1622,58],{"class":57},[34,1624,1625],{"class":36,"line":61},[34,1626,65],{"emptyLinePlaceholder":64},[34,1628,1629,1631,1633,1635,1637],{"class":36,"line":68},[34,1630,95],{"class":40},[34,1632,531],{"class":98},[34,1634,534],{"class":40},[34,1636,1331],{"class":105},[34,1638,833],{"class":44},[34,1640,1641,1643,1645,1647,1649],{"class":36,"line":75},[34,1642,95],{"class":40},[34,1644,1309],{"class":98},[34,1646,534],{"class":40},[34,1648,1314],{"class":105},[34,1650,833],{"class":44},[34,1652,1653],{"class":36,"line":81},[34,1654,65],{"emptyLinePlaceholder":64},[34,1656,1657],{"class":36,"line":87},[34,1658,1659],{"class":71},"\u002F\u002F ── Push: add to history stack (back button returns) ──\n",[34,1661,1662,1665,1668,1670,1673],{"class":36,"line":92},[34,1663,1664],{"class":44},"router.",[34,1666,1667],{"class":105},"push",[34,1669,168],{"class":44},[34,1671,1672],{"class":57},"'\u002Fusers\u002F42'",[34,1674,1128],{"class":44},[34,1676,1677,1679,1681,1684,1686,1689,1692,1695],{"class":36,"line":118},[34,1678,1664],{"class":44},[34,1680,1667],{"class":105},[34,1682,1683],{"class":44},"({ name: ",[34,1685,413],{"class":57},[34,1687,1688],{"class":44},", params: { id: ",[34,1690,1691],{"class":57},"'42'",[34,1693,1694],{"class":44}," } })  ",[34,1696,1697],{"class":71},"\u002F\u002F by name (preferred)\n",[34,1699,1700,1702,1704,1707,1710,1713,1716,1719,1722],{"class":36,"line":124},[34,1701,1664],{"class":44},[34,1703,1667],{"class":105},[34,1705,1706],{"class":44},"({ path: ",[34,1708,1709],{"class":57},"'\u002Fsearch'",[34,1711,1712],{"class":44},", query: { q: ",[34,1714,1715],{"class":57},"'vue'",[34,1717,1718],{"class":44},", page: ",[34,1720,1721],{"class":57},"'2'",[34,1723,1724],{"class":44}," } })\n",[34,1726,1727],{"class":36,"line":136},[34,1728,65],{"emptyLinePlaceholder":64},[34,1730,1731],{"class":36,"line":147},[34,1732,1733],{"class":71},"\u002F\u002F ── Replace: replace current entry (back button skips this page) ──\n",[34,1735,1736],{"class":36,"line":153},[34,1737,1738],{"class":71},"\u002F\u002F Use after login: don't want back button to return to the login page\n",[34,1740,1741,1743,1746,1748,1750],{"class":36,"line":177},[34,1742,1664],{"class":44},[34,1744,1745],{"class":105},"replace",[34,1747,1683],{"class":44},[34,1749,203],{"class":57},[34,1751,1752],{"class":44}," })\n",[34,1754,1755],{"class":36,"line":183},[34,1756,65],{"emptyLinePlaceholder":64},[34,1758,1759],{"class":36,"line":188},[34,1760,1761],{"class":71},"\u002F\u002F ── Query params: always strings — type coercion needed ──\n",[34,1763,1764,1766,1769,1771,1774,1777,1779,1781,1783,1786],{"class":36,"line":198},[34,1765,95],{"class":40},[34,1767,1768],{"class":98}," page",[34,1770,534],{"class":40},[34,1772,1773],{"class":105}," parseInt",[34,1775,1776],{"class":44},"(route.query.page ",[34,1778,1122],{"class":40},[34,1780,1125],{"class":98},[34,1782,812],{"class":44},[34,1784,1785],{"class":40},"||",[34,1787,1788],{"class":98}," 1\n",[34,1790,1791,1793,1796,1798,1801,1803],{"class":36,"line":208},[34,1792,95],{"class":40},[34,1794,1795],{"class":98}," q",[34,1797,534],{"class":40},[34,1799,1800],{"class":44}," route.query.q ",[34,1802,1122],{"class":40},[34,1804,1805],{"class":98}," string\n",[34,1807,1808],{"class":36,"line":214},[34,1809,65],{"emptyLinePlaceholder":64},[34,1811,1812],{"class":36,"line":235},[34,1813,1814],{"class":71},"\u002F\u002F ── Dynamic params: always strings — even if defined as \u002Fusers\u002F:id ──\n",[34,1816,1817,1819,1822,1824,1827],{"class":36,"line":241},[34,1818,95],{"class":40},[34,1820,1821],{"class":98}," userId",[34,1823,534],{"class":40},[34,1825,1826],{"class":44}," route.params.id  ",[34,1828,1829],{"class":71},"\u002F\u002F \"42\" (string), NOT 42 (number)\n",[34,1831,1832],{"class":36,"line":259},[34,1833,65],{"emptyLinePlaceholder":64},[34,1835,1836],{"class":36,"line":265},[34,1837,1838],{"class":71},"\u002F\u002F ── Go: relative history movement ──\n",[34,1840,1841,1843,1846,1848,1851,1854,1857],{"class":36,"line":271},[34,1842,1664],{"class":44},[34,1844,1845],{"class":105},"go",[34,1847,168],{"class":44},[34,1849,1850],{"class":40},"-",[34,1852,1853],{"class":98},"1",[34,1855,1856],{"class":44},")  ",[34,1858,1859],{"class":71},"\u002F\u002F back\n",[34,1861,1862,1864,1866,1868,1870,1873],{"class":36,"line":277},[34,1863,1664],{"class":44},[34,1865,1845],{"class":105},[34,1867,168],{"class":44},[34,1869,1853],{"class":98},[34,1871,1872],{"class":44},")   ",[34,1874,1875],{"class":71},"\u002F\u002F forward\n",[34,1877,1878,1880,1882,1884,1886,1889,1891],{"class":36,"line":292},[34,1879,1664],{"class":44},[34,1881,1845],{"class":105},[34,1883,168],{"class":44},[34,1885,1850],{"class":40},[34,1887,1888],{"class":98},"2",[34,1890,1856],{"class":44},[34,1892,1893],{"class":71},"\u002F\u002F back two pages\n",[34,1895,1896],{"class":36,"line":303},[34,1897,65],{"emptyLinePlaceholder":64},[34,1899,1900],{"class":36,"line":322},[34,1901,1902],{"class":71},"\u002F\u002F ── Async navigation: returns a promise ──\n",[34,1904,1905,1907,1910,1912,1914,1916,1918],{"class":36,"line":328},[34,1906,1563],{"class":40},[34,1908,1909],{"class":44}," router.",[34,1911,1667],{"class":105},[34,1913,168],{"class":44},[34,1915,193],{"class":57},[34,1917,1856],{"class":44},[34,1919,1920],{"class":71},"\u002F\u002F resolves after navigation completes\n",[15,1922,1924],{"id":1923},"type-safe-routes-vue-router-4x-typescript","Type-Safe Routes (Vue Router 4.x + TypeScript)",[20,1926,1928],{"filename":1927,"language":23},"typed-routes.ts",[25,1929,1931],{"className":27,"code":1930,"language":23,"meta":29,"style":29},"import 'vue-router'\n\n\u002F\u002F ── Module augmentation: type-safe route names and params ──\ndeclare module 'vue-router' {\n  interface RouteMeta {\n    requiresAuth?: boolean\n    title?: string\n    roles?: string[]\n  }\n\n  \u002F\u002F Type-safe route names: typo in router.push({ name: 'dasboard' }) → TS error\n  interface RouteNamedMap {\n    home: RouteRecordInfo\u003C'home', '\u002F', {}, {}>\n    user: RouteRecordInfo\u003C'user', '\u002Fusers\u002F:id', { id: string }, {}>\n    dashboard: RouteRecordInfo\u003C'dashboard', '\u002Fdashboard', {}, {}>\n  }\n}\n\n\u002F\u002F ── Now router.push is type-checked: ──\n\u002F\u002F router.push({ name: 'user', params: { id: '42' } })  ✅\n\u002F\u002F router.push({ name: 'user', params: { id: 42 } })      ❌ id must be string\n\u002F\u002F router.push({ name: 'dasboard' })                     ❌ typo caught\n\n\u002F\u002F ── route.meta is also typed: ──\n\u002F\u002F if (route.meta.requiresAuth) { ... }  ✅ boolean | undefined\n\u002F\u002F route.meta.unknownProp                        ❌ not in interface\n",[31,1932,1933,1939,1943,1948,1961,1971,1982,1991,2003,2007,2011,2016,2025,2046,2075,2094,2098,2102,2106,2111,2116,2121,2126,2130,2135,2140],{"__ignoreMap":29},[34,1934,1935,1937],{"class":36,"line":37},[34,1936,41],{"class":40},[34,1938,58],{"class":57},[34,1940,1941],{"class":36,"line":61},[34,1942,65],{"emptyLinePlaceholder":64},[34,1944,1945],{"class":36,"line":68},[34,1946,1947],{"class":71},"\u002F\u002F ── Module augmentation: type-safe route names and params ──\n",[34,1949,1950,1953,1956,1959],{"class":36,"line":75},[34,1951,1952],{"class":40},"declare",[34,1954,1955],{"class":40}," module",[34,1957,1958],{"class":57}," 'vue-router'",[34,1960,817],{"class":44},[34,1962,1963,1966,1969],{"class":36,"line":81},[34,1964,1965],{"class":40},"  interface",[34,1967,1968],{"class":105}," RouteMeta",[34,1970,817],{"class":44},[34,1972,1973,1976,1979],{"class":36,"line":87},[34,1974,1975],{"class":590},"    requiresAuth",[34,1977,1978],{"class":40},"?:",[34,1980,1981],{"class":98}," boolean\n",[34,1983,1984,1987,1989],{"class":36,"line":92},[34,1985,1986],{"class":590},"    title",[34,1988,1978],{"class":40},[34,1990,1805],{"class":98},[34,1992,1993,1996,1998,2000],{"class":36,"line":118},[34,1994,1995],{"class":590},"    roles",[34,1997,1978],{"class":40},[34,1999,1125],{"class":98},[34,2001,2002],{"class":44},"[]\n",[34,2004,2005],{"class":36,"line":124},[34,2006,1501],{"class":44},[34,2008,2009],{"class":36,"line":136},[34,2010,65],{"emptyLinePlaceholder":64},[34,2012,2013],{"class":36,"line":147},[34,2014,2015],{"class":71},"  \u002F\u002F Type-safe route names: typo in router.push({ name: 'dasboard' }) → TS error\n",[34,2017,2018,2020,2023],{"class":36,"line":153},[34,2019,1965],{"class":40},[34,2021,2022],{"class":105}," RouteNamedMap",[34,2024,817],{"class":44},[34,2026,2027,2030,2032,2035,2037,2039,2041,2043],{"class":36,"line":177},[34,2028,2029],{"class":590},"    home",[34,2031,102],{"class":40},[34,2033,2034],{"class":105}," RouteRecordInfo",[34,2036,1265],{"class":44},[34,2038,142],{"class":57},[34,2040,594],{"class":44},[34,2042,130],{"class":57},[34,2044,2045],{"class":44},", {}, {}>\n",[34,2047,2048,2051,2053,2055,2057,2059,2061,2063,2066,2068,2070,2072],{"class":36,"line":183},[34,2049,2050],{"class":590},"    user",[34,2052,102],{"class":40},[34,2054,2034],{"class":105},[34,2056,1265],{"class":44},[34,2058,413],{"class":57},[34,2060,594],{"class":44},[34,2062,403],{"class":57},[34,2064,2065],{"class":44},", { ",[34,2067,1374],{"class":590},[34,2069,102],{"class":40},[34,2071,1125],{"class":98},[34,2073,2074],{"class":44}," }, {}>\n",[34,2076,2077,2080,2082,2084,2086,2088,2090,2092],{"class":36,"line":188},[34,2078,2079],{"class":590},"    dashboard",[34,2081,102],{"class":40},[34,2083,2034],{"class":105},[34,2085,1265],{"class":44},[34,2087,203],{"class":57},[34,2089,594],{"class":44},[34,2091,193],{"class":57},[34,2093,2045],{"class":44},[34,2095,2096],{"class":36,"line":198},[34,2097,1501],{"class":44},[34,2099,2100],{"class":36,"line":208},[34,2101,1246],{"class":44},[34,2103,2104],{"class":36,"line":214},[34,2105,65],{"emptyLinePlaceholder":64},[34,2107,2108],{"class":36,"line":235},[34,2109,2110],{"class":71},"\u002F\u002F ── Now router.push is type-checked: ──\n",[34,2112,2113],{"class":36,"line":241},[34,2114,2115],{"class":71},"\u002F\u002F router.push({ name: 'user', params: { id: '42' } })  ✅\n",[34,2117,2118],{"class":36,"line":259},[34,2119,2120],{"class":71},"\u002F\u002F router.push({ name: 'user', params: { id: 42 } })      ❌ id must be string\n",[34,2122,2123],{"class":36,"line":265},[34,2124,2125],{"class":71},"\u002F\u002F router.push({ name: 'dasboard' })                     ❌ typo caught\n",[34,2127,2128],{"class":36,"line":271},[34,2129,65],{"emptyLinePlaceholder":64},[34,2131,2132],{"class":36,"line":277},[34,2133,2134],{"class":71},"\u002F\u002F ── route.meta is also typed: ──\n",[34,2136,2137],{"class":36,"line":292},[34,2138,2139],{"class":71},"\u002F\u002F if (route.meta.requiresAuth) { ... }  ✅ boolean | undefined\n",[34,2141,2142],{"class":36,"line":303},[34,2143,2144],{"class":71},"\u002F\u002F route.meta.unknownProp                        ❌ not in interface\n",[15,2146,2148],{"id":2147},"tips-tricks","💡 Tips & Tricks",[20,2150,2152],{"filename":2151,"language":23},"tips.ts",[25,2153,2155],{"className":27,"code":2154,"language":23,"meta":29,"style":29},"\u002F\u002F ── 1. useRoute() is reactive — route.params updates on navigation ──\n\u002F\u002F Don't destructure: const { id } = route.params → not reactive\n\u002F\u002F Use: route.params.id directly in template, or watch(() => route.params.id)\n\n\u002F\u002F ── 2. Lazy loading with loading state ──\nconst routes = [{\n  path: '\u002Fadmin',\n  component: () => import('@\u002Fviews\u002FAdminView.vue'),\n  \u002F\u002F The promise resolves when the chunk loads; shows fallback until then\n}]\n\n\u002F\u002F ── 3. Route-level data fetching with beforeEnter ──\n{\n  path: '\u002Fpost\u002F:id',\n  component: PostView,\n  beforeEnter: async (to) => {\n    \u002F\u002F Fetch data before component loads — available in route.meta\n    const res = await fetch(`\u002Fapi\u002Fposts\u002F${to.params.id}`)\n    to.meta.post = await res.json()\n  }\n}\n\n\u002F\u002F ── 4. Scroll position: savedPosition for back\u002Fforward, explicit for push ──\nscrollBehavior(to, from, savedPosition) {\n  return savedPosition ?? { top: 0 }\n}\n\n\u002F\u002F ── 5. Route key: force re-creation on param change ──\n\u002F\u002F \u003CRouterView :route-key=\"route.fullPath\" \u002F> → component re-mounts on every change\n\u002F\u002F Default: component is reused (only params change, hooks fire, not re-mount)\n",[31,2156,2157,2162,2167,2172,2176,2181,2192,2202,2220,2225,2230,2234,2239,2244,2257,2264,2283,2288,2320,2335,2339,2343,2347,2352,2360,2377,2381,2385,2390,2395],{"__ignoreMap":29},[34,2158,2159],{"class":36,"line":37},[34,2160,2161],{"class":71},"\u002F\u002F ── 1. useRoute() is reactive — route.params updates on navigation ──\n",[34,2163,2164],{"class":36,"line":61},[34,2165,2166],{"class":71},"\u002F\u002F Don't destructure: const { id } = route.params → not reactive\n",[34,2168,2169],{"class":36,"line":68},[34,2170,2171],{"class":71},"\u002F\u002F Use: route.params.id directly in template, or watch(() => route.params.id)\n",[34,2173,2174],{"class":36,"line":75},[34,2175,65],{"emptyLinePlaceholder":64},[34,2177,2178],{"class":36,"line":81},[34,2179,2180],{"class":71},"\u002F\u002F ── 2. Lazy loading with loading state ──\n",[34,2182,2183,2185,2187,2189],{"class":36,"line":87},[34,2184,95],{"class":40},[34,2186,99],{"class":98},[34,2188,534],{"class":40},[34,2190,2191],{"class":44}," [{\n",[34,2193,2194,2197,2200],{"class":36,"line":92},[34,2195,2196],{"class":44},"  path: ",[34,2198,2199],{"class":57},"'\u002Fadmin'",[34,2201,133],{"class":44},[34,2203,2204,2207,2209,2211,2213,2215,2218],{"class":36,"line":118},[34,2205,2206],{"class":105},"  component",[34,2208,159],{"class":44},[34,2210,162],{"class":40},[34,2212,165],{"class":40},[34,2214,168],{"class":44},[34,2216,2217],{"class":57},"'@\u002Fviews\u002FAdminView.vue'",[34,2219,174],{"class":44},[34,2221,2222],{"class":36,"line":124},[34,2223,2224],{"class":71},"  \u002F\u002F The promise resolves when the chunk loads; shows fallback until then\n",[34,2226,2227],{"class":36,"line":136},[34,2228,2229],{"class":44},"}]\n",[34,2231,2232],{"class":36,"line":147},[34,2233,65],{"emptyLinePlaceholder":64},[34,2235,2236],{"class":36,"line":153},[34,2237,2238],{"class":71},"\u002F\u002F ── 3. Route-level data fetching with beforeEnter ──\n",[34,2240,2241],{"class":36,"line":177},[34,2242,2243],{"class":44},"{\n",[34,2245,2246,2249,2252,2255],{"class":36,"line":183},[34,2247,2248],{"class":105},"  path",[34,2250,2251],{"class":44},": ",[34,2253,2254],{"class":57},"'\u002Fpost\u002F:id'",[34,2256,133],{"class":44},[34,2258,2259,2261],{"class":36,"line":188},[34,2260,2206],{"class":105},[34,2262,2263],{"class":44},": PostView,\n",[34,2265,2266,2269,2271,2273,2275,2277,2279,2281],{"class":36,"line":198},[34,2267,2268],{"class":105},"  beforeEnter",[34,2270,2251],{"class":44},[34,2272,800],{"class":40},[34,2274,803],{"class":44},[34,2276,591],{"class":590},[34,2278,812],{"class":44},[34,2280,162],{"class":40},[34,2282,817],{"class":44},[34,2284,2285],{"class":36,"line":208},[34,2286,2287],{"class":71},"    \u002F\u002F Fetch data before component loads — available in route.meta\n",[34,2289,2290,2292,2294,2296,2298,2300,2302,2305,2307,2309,2312,2314,2316,2318],{"class":36,"line":214},[34,2291,822],{"class":40},[34,2293,1384],{"class":98},[34,2295,534],{"class":40},[34,2297,1389],{"class":40},[34,2299,1392],{"class":105},[34,2301,168],{"class":44},[34,2303,2304],{"class":57},"`\u002Fapi\u002Fposts\u002F${",[34,2306,591],{"class":44},[34,2308,556],{"class":57},[34,2310,2311],{"class":44},"params",[34,2313,556],{"class":57},[34,2315,1374],{"class":44},[34,2317,1402],{"class":57},[34,2319,1128],{"class":44},[34,2321,2322,2325,2327,2329,2331,2333],{"class":36,"line":235},[34,2323,2324],{"class":44},"    to.meta.post ",[34,2326,112],{"class":40},[34,2328,1389],{"class":40},[34,2330,1416],{"class":44},[34,2332,1419],{"class":105},[34,2334,833],{"class":44},[34,2336,2337],{"class":36,"line":241},[34,2338,1501],{"class":44},[34,2340,2341],{"class":36,"line":259},[34,2342,1246],{"class":44},[34,2344,2345],{"class":36,"line":265},[34,2346,65],{"emptyLinePlaceholder":64},[34,2348,2349],{"class":36,"line":271},[34,2350,2351],{"class":71},"\u002F\u002F ── 4. Scroll position: savedPosition for back\u002Fforward, explicit for push ──\n",[34,2353,2354,2357],{"class":36,"line":277},[34,2355,2356],{"class":105},"scrollBehavior",[34,2358,2359],{"class":44},"(to, from, savedPosition) {\n",[34,2361,2362,2365,2368,2371,2373,2375],{"class":36,"line":292},[34,2363,2364],{"class":40},"  return",[34,2366,2367],{"class":44}," savedPosition ",[34,2369,2370],{"class":40},"??",[34,2372,665],{"class":44},[34,2374,668],{"class":98},[34,2376,650],{"class":44},[34,2378,2379],{"class":36,"line":303},[34,2380,1246],{"class":44},[34,2382,2383],{"class":36,"line":322},[34,2384,65],{"emptyLinePlaceholder":64},[34,2386,2387],{"class":36,"line":328},[34,2388,2389],{"class":71},"\u002F\u002F ── 5. Route key: force re-creation on param change ──\n",[34,2391,2392],{"class":36,"line":333},[34,2393,2394],{"class":71},"\u002F\u002F \u003CRouterView :route-key=\"route.fullPath\" \u002F> → component re-mounts on every change\n",[34,2396,2397],{"class":36,"line":343},[34,2398,2399],{"class":71},"\u002F\u002F Default: component is reused (only params change, hooks fire, not re-mount)\n",[15,2401,2403],{"id":2402},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[20,2405,2407],{"filename":2406,"language":23},"edge-cases.ts",[25,2408,2410],{"className":27,"code":2409,"language":23,"meta":29,"style":29},"\u002F\u002F ── 1. Dynamic params are ALWAYS strings ──\n\u002F\u002F path: '\u002Fusers\u002F:id' → route.params.id is \"42\" (string), not 42 (number)\n\u002F\u002F Even with .number in path: '\u002Fusers\u002F:id(\\\\d+)' → still string\n\n\u002F\u002F ── 2. Same-component navigation doesn't re-mount ──\n\u002F\u002F \u002Fusers\u002F1 → \u002Fusers\u002F2: same UserView component, only params change.\n\u002F\u002F onMounted does NOT fire again. Use onBeforeRouteUpdate or watch route.params.\n\n\u002F\u002F ── 3. Query params are not in route.matched ──\n\u002F\u002F meta is on route records; query is on the route object itself.\n\u002F\u002F to.matched.some(r => r.meta.x) checks route records, not query params.\n\n\u002F\u002F ── 4. Hash mode vs history mode — URL format differs ──\n\u002F\u002F History: example.com\u002Fusers\u002F42 → clean URL, needs server config (fallback to index.html)\n\u002F\u002F Hash: example.com\u002F#\u002Fusers\u002F42 → no server config, but URLs have #, worse SEO\n\n\u002F\u002F ── 5. beforeEach guard order: global → parent → child ──\n\u002F\u002F Multiple guards fire in order: global beforeEach, per-route beforeEnter,\n\u002F\u002F in-component beforeRouteEnter. If any returns false, later ones don't run.\n\n\u002F\u002F ── 6. Navigation cancellation is silent ──\n\u002F\u002F If a guard returns false, router.push() resolves (not rejects).\n\u002F\u002F Check the return: const failure = await router.push('\u002Fx'); if (failure) { ... }\n",[31,2411,2412,2417,2422,2427,2431,2436,2441,2446,2450,2455,2460,2465,2469,2474,2479,2484,2488,2493,2498,2503,2507,2512,2517],{"__ignoreMap":29},[34,2413,2414],{"class":36,"line":37},[34,2415,2416],{"class":71},"\u002F\u002F ── 1. Dynamic params are ALWAYS strings ──\n",[34,2418,2419],{"class":36,"line":61},[34,2420,2421],{"class":71},"\u002F\u002F path: '\u002Fusers\u002F:id' → route.params.id is \"42\" (string), not 42 (number)\n",[34,2423,2424],{"class":36,"line":68},[34,2425,2426],{"class":71},"\u002F\u002F Even with .number in path: '\u002Fusers\u002F:id(\\\\d+)' → still string\n",[34,2428,2429],{"class":36,"line":75},[34,2430,65],{"emptyLinePlaceholder":64},[34,2432,2433],{"class":36,"line":81},[34,2434,2435],{"class":71},"\u002F\u002F ── 2. Same-component navigation doesn't re-mount ──\n",[34,2437,2438],{"class":36,"line":87},[34,2439,2440],{"class":71},"\u002F\u002F \u002Fusers\u002F1 → \u002Fusers\u002F2: same UserView component, only params change.\n",[34,2442,2443],{"class":36,"line":92},[34,2444,2445],{"class":71},"\u002F\u002F onMounted does NOT fire again. Use onBeforeRouteUpdate or watch route.params.\n",[34,2447,2448],{"class":36,"line":118},[34,2449,65],{"emptyLinePlaceholder":64},[34,2451,2452],{"class":36,"line":124},[34,2453,2454],{"class":71},"\u002F\u002F ── 3. Query params are not in route.matched ──\n",[34,2456,2457],{"class":36,"line":136},[34,2458,2459],{"class":71},"\u002F\u002F meta is on route records; query is on the route object itself.\n",[34,2461,2462],{"class":36,"line":147},[34,2463,2464],{"class":71},"\u002F\u002F to.matched.some(r => r.meta.x) checks route records, not query params.\n",[34,2466,2467],{"class":36,"line":153},[34,2468,65],{"emptyLinePlaceholder":64},[34,2470,2471],{"class":36,"line":177},[34,2472,2473],{"class":71},"\u002F\u002F ── 4. Hash mode vs history mode — URL format differs ──\n",[34,2475,2476],{"class":36,"line":183},[34,2477,2478],{"class":71},"\u002F\u002F History: example.com\u002Fusers\u002F42 → clean URL, needs server config (fallback to index.html)\n",[34,2480,2481],{"class":36,"line":188},[34,2482,2483],{"class":71},"\u002F\u002F Hash: example.com\u002F#\u002Fusers\u002F42 → no server config, but URLs have #, worse SEO\n",[34,2485,2486],{"class":36,"line":198},[34,2487,65],{"emptyLinePlaceholder":64},[34,2489,2490],{"class":36,"line":208},[34,2491,2492],{"class":71},"\u002F\u002F ── 5. beforeEach guard order: global → parent → child ──\n",[34,2494,2495],{"class":36,"line":214},[34,2496,2497],{"class":71},"\u002F\u002F Multiple guards fire in order: global beforeEach, per-route beforeEnter,\n",[34,2499,2500],{"class":36,"line":235},[34,2501,2502],{"class":71},"\u002F\u002F in-component beforeRouteEnter. If any returns false, later ones don't run.\n",[34,2504,2505],{"class":36,"line":241},[34,2506,65],{"emptyLinePlaceholder":64},[34,2508,2509],{"class":36,"line":259},[34,2510,2511],{"class":71},"\u002F\u002F ── 6. Navigation cancellation is silent ──\n",[34,2513,2514],{"class":36,"line":265},[34,2515,2516],{"class":71},"\u002F\u002F If a guard returns false, router.push() resolves (not rejects).\n",[34,2518,2519],{"class":36,"line":271},[34,2520,2521],{"class":71},"\u002F\u002F Check the return: const failure = await router.push('\u002Fx'); if (failure) { ... }\n",[15,2523,2525],{"id":2524},"spot-the-bug","🧠 Spot the Bug",[2527,2528,2529,2530,2533,2534,2537],"p",{},"Navigating from ",[31,2531,2532],{},"\u002Fusers\u002F1"," to ",[31,2535,2536],{},"\u002Fusers\u002F2"," doesn't refresh the displayed user data.",[20,2539,2541],{"filename":2540,"language":1254},"RouteBug.vue",[25,2542,2544],{"className":1257,"code":2543,"language":1254,"meta":29,"style":29},"\u003Cscript setup>\nimport { ref, onMounted } from 'vue'\nimport { useRoute } from 'vue-router'\n\nconst route = useRoute()\nconst user = ref(null)\n\n\u002F\u002F onMounted fires only ONCE — component is reused for \u002Fusers\u002F2\nonMounted(async () => {\n  const res = await fetch(`\u002Fapi\u002Fusers\u002F${route.params.id}`)\n  user.value = await res.json()\n})\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cdiv>{{ user?.name }}\u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n",[31,2545,2546,2556,2567,2578,2582,2594,2610,2614,2619,2635,2666,2680,2684,2692,2696,2705,2720],{"__ignoreMap":29},[34,2547,2548,2550,2552,2554],{"class":36,"line":37},[34,2549,1265],{"class":44},[34,2551,1269],{"class":1268},[34,2553,1272],{"class":105},[34,2555,1275],{"class":44},[34,2557,2558,2560,2563,2565],{"class":36,"line":61},[34,2559,41],{"class":40},[34,2561,2562],{"class":44}," { ref, onMounted } ",[34,2564,54],{"class":40},[34,2566,1298],{"class":57},[34,2568,2569,2571,2574,2576],{"class":36,"line":68},[34,2570,41],{"class":40},[34,2572,2573],{"class":44}," { useRoute } ",[34,2575,54],{"class":40},[34,2577,58],{"class":57},[34,2579,2580],{"class":36,"line":75},[34,2581,65],{"emptyLinePlaceholder":64},[34,2583,2584,2586,2588,2590,2592],{"class":36,"line":81},[34,2585,95],{"class":40},[34,2587,1309],{"class":98},[34,2589,534],{"class":40},[34,2591,1314],{"class":105},[34,2593,833],{"class":44},[34,2595,2596,2598,2600,2602,2604,2606,2608],{"class":36,"line":87},[34,2597,95],{"class":40},[34,2599,1344],{"class":98},[34,2601,534],{"class":40},[34,2603,1349],{"class":105},[34,2605,168],{"class":44},[34,2607,1354],{"class":98},[34,2609,1128],{"class":44},[34,2611,2612],{"class":36,"line":92},[34,2613,65],{"emptyLinePlaceholder":64},[34,2615,2616],{"class":36,"line":118},[34,2617,2618],{"class":71},"\u002F\u002F onMounted fires only ONCE — component is reused for \u002Fusers\u002F2\n",[34,2620,2621,2624,2626,2628,2631,2633],{"class":36,"line":124},[34,2622,2623],{"class":105},"onMounted",[34,2625,168],{"class":44},[34,2627,800],{"class":40},[34,2629,2630],{"class":44}," () ",[34,2632,162],{"class":40},[34,2634,817],{"class":44},[34,2636,2637,2639,2641,2643,2645,2647,2649,2651,2654,2656,2658,2660,2662,2664],{"class":36,"line":136},[34,2638,1381],{"class":40},[34,2640,1384],{"class":98},[34,2642,534],{"class":40},[34,2644,1389],{"class":40},[34,2646,1392],{"class":105},[34,2648,168],{"class":44},[34,2650,1397],{"class":57},[34,2652,2653],{"class":44},"route",[34,2655,556],{"class":57},[34,2657,2311],{"class":44},[34,2659,556],{"class":57},[34,2661,1374],{"class":44},[34,2663,1402],{"class":57},[34,2665,1128],{"class":44},[34,2667,2668,2670,2672,2674,2676,2678],{"class":36,"line":147},[34,2669,1409],{"class":44},[34,2671,112],{"class":40},[34,2673,1389],{"class":40},[34,2675,1416],{"class":44},[34,2677,1419],{"class":105},[34,2679,833],{"class":44},[34,2681,2682],{"class":36,"line":153},[34,2683,686],{"class":44},[34,2685,2686,2688,2690],{"class":36,"line":177},[34,2687,1595],{"class":44},[34,2689,1269],{"class":1268},[34,2691,1275],{"class":44},[34,2693,2694],{"class":36,"line":183},[34,2695,65],{"emptyLinePlaceholder":64},[34,2697,2698,2700,2703],{"class":36,"line":188},[34,2699,1265],{"class":44},[34,2701,2702],{"class":1268},"template",[34,2704,1275],{"class":44},[34,2706,2707,2710,2713,2716,2718],{"class":36,"line":198},[34,2708,2709],{"class":44},"  \u003C",[34,2711,2712],{"class":1268},"div",[34,2714,2715],{"class":44},">{{ user?.name }}\u003C\u002F",[34,2717,2712],{"class":1268},[34,2719,1275],{"class":44},[34,2721,2722,2724,2726],{"class":36,"line":208},[34,2723,1595],{"class":44},[34,2725,2702],{"class":1268},[34,2727,1275],{"class":44},[2729,2730,2731,2735,2754,2767,2955],"details",{},[2732,2733,2734],"summary",{},"Answer",[2527,2736,2737,2738,2740,2741,2743,2744,2747,2748,2750,2751,2753],{},"When navigating between ",[31,2739,2532],{}," and ",[31,2742,2536],{},", Vue Router reuses the same ",[31,2745,2746],{},"UserView"," component instance (only the ",[31,2749,2311],{}," change). ",[31,2752,2623],{}," fires only on the initial mount — it does not fire again on param-only changes.",[2527,2755,2756,2760,2761,2763,2764,2766],{},[2757,2758,2759],"strong",{},"Fix"," — use ",[31,2762,1449],{}," or ",[31,2765,1524],{}," the route param:",[20,2768,2770],{"filename":2769,"language":1254},"RouteFixed.vue",[25,2771,2773],{"className":1257,"code":2772,"language":1254,"meta":29,"style":29},"\u003Cscript setup>\nimport { ref, watch } from 'vue'\nimport { useRoute } from 'vue-router'\n\nconst route = useRoute()\nconst user = ref(null)\n\nasync function fetchUser(id) {\n  const res = await fetch(`\u002Fapi\u002Fusers\u002F${id}`)\n  user.value = await res.json()\n}\n\n\u002F\u002F Watch fires whenever route.params.id changes (including initial load with immediate)\nwatch(() => route.params.id, (id) => {\n  if (id) fetchUser(id)\n}, { immediate: true })\n\u003C\u002Fscript>\n",[31,2774,2775,2785,2796,2806,2810,2822,2838,2842,2856,2878,2892,2896,2900,2905,2925,2938,2947],{"__ignoreMap":29},[34,2776,2777,2779,2781,2783],{"class":36,"line":37},[34,2778,1265],{"class":44},[34,2780,1269],{"class":1268},[34,2782,1272],{"class":105},[34,2784,1275],{"class":44},[34,2786,2787,2789,2792,2794],{"class":36,"line":61},[34,2788,41],{"class":40},[34,2790,2791],{"class":44}," { ref, watch } ",[34,2793,54],{"class":40},[34,2795,1298],{"class":57},[34,2797,2798,2800,2802,2804],{"class":36,"line":68},[34,2799,41],{"class":40},[34,2801,2573],{"class":44},[34,2803,54],{"class":40},[34,2805,58],{"class":57},[34,2807,2808],{"class":36,"line":75},[34,2809,65],{"emptyLinePlaceholder":64},[34,2811,2812,2814,2816,2818,2820],{"class":36,"line":81},[34,2813,95],{"class":40},[34,2815,1309],{"class":98},[34,2817,534],{"class":40},[34,2819,1314],{"class":105},[34,2821,833],{"class":44},[34,2823,2824,2826,2828,2830,2832,2834,2836],{"class":36,"line":87},[34,2825,95],{"class":40},[34,2827,1344],{"class":98},[34,2829,534],{"class":40},[34,2831,1349],{"class":105},[34,2833,168],{"class":44},[34,2835,1354],{"class":98},[34,2837,1128],{"class":44},[34,2839,2840],{"class":36,"line":92},[34,2841,65],{"emptyLinePlaceholder":64},[34,2843,2844,2846,2848,2850,2852,2854],{"class":36,"line":118},[34,2845,800],{"class":40},[34,2847,772],{"class":40},[34,2849,1369],{"class":105},[34,2851,168],{"class":44},[34,2853,1374],{"class":590},[34,2855,604],{"class":44},[34,2857,2858,2860,2862,2864,2866,2868,2870,2872,2874,2876],{"class":36,"line":124},[34,2859,1381],{"class":40},[34,2861,1384],{"class":98},[34,2863,534],{"class":40},[34,2865,1389],{"class":40},[34,2867,1392],{"class":105},[34,2869,168],{"class":44},[34,2871,1397],{"class":57},[34,2873,1374],{"class":44},[34,2875,1402],{"class":57},[34,2877,1128],{"class":44},[34,2879,2880,2882,2884,2886,2888,2890],{"class":36,"line":136},[34,2881,1409],{"class":44},[34,2883,112],{"class":40},[34,2885,1389],{"class":40},[34,2887,1416],{"class":44},[34,2889,1419],{"class":105},[34,2891,833],{"class":44},[34,2893,2894],{"class":36,"line":147},[34,2895,1246],{"class":44},[34,2897,2898],{"class":36,"line":153},[34,2899,65],{"emptyLinePlaceholder":64},[34,2901,2902],{"class":36,"line":177},[34,2903,2904],{"class":71},"\u002F\u002F Watch fires whenever route.params.id changes (including initial load with immediate)\n",[34,2906,2907,2909,2912,2914,2917,2919,2921,2923],{"class":36,"line":183},[34,2908,1524],{"class":105},[34,2910,2911],{"class":44},"(() ",[34,2913,162],{"class":40},[34,2915,2916],{"class":44}," route.params.id, (",[34,2918,1374],{"class":590},[34,2920,812],{"class":44},[34,2922,162],{"class":40},[34,2924,817],{"class":44},[34,2926,2927,2929,2932,2935],{"class":36,"line":188},[34,2928,1477],{"class":40},[34,2930,2931],{"class":44}," (id) ",[34,2933,2934],{"class":105},"fetchUser",[34,2936,2937],{"class":44},"(id)\n",[34,2939,2940,2943,2945],{"class":36,"line":198},[34,2941,2942],{"class":44},"}, { immediate: ",[34,2944,247],{"class":98},[34,2946,1752],{"class":44},[34,2948,2949,2951,2953],{"class":36,"line":208},[34,2950,1595],{"class":44},[34,2952,1269],{"class":1268},[34,2954,1275],{"class":44},[2527,2956,2957,2960,2961,2963,2964,2763,2967,2969],{},[2757,2958,2959],{},"The lesson",": same-route navigation (only params change) reuses the component instance — lifecycle hooks don't re-fire. Use ",[31,2962,1524],{}," on ",[31,2965,2966],{},"route.params",[31,2968,1449],{}," to react to param changes.",[2971,2972,2973],"style",{},"html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .sk71V, html code.shiki .sk71V{--shiki-default:#22863A;--shiki-github-dark:#85E89D}",{"title":29,"searchDepth":61,"depth":61,"links":2975},[2976,2977,2978,2979,2980,2981,2982,2983],{"id":17,"depth":61,"text":18},{"id":689,"depth":61,"text":690},{"id":1249,"depth":61,"text":1250},{"id":1602,"depth":61,"text":1603},{"id":1923,"depth":61,"text":1924},{"id":2147,"depth":61,"text":2148},{"id":2402,"depth":61,"text":2403},{"id":2524,"depth":61,"text":2525},"Route guards composition, lazy loading with code splitting, scroll behavior restoration, nested route params, beforeRouteUpdate vs watch $route, and type-safe route definitions.","md",{},"\u002Fvue\u002F11-routing-vue-router",{"title":5,"description":2984},"vue\u002F11-routing-vue-router","0DEQclXA_PeAsvozN_zykqCpDIs7o94BRC36L0gxdJg",1789924655547]