In Vue Router 5, the mail client's fade never plays between messages, and after adding :key="route.fullPath" the settings sidebar resets on every tab; how do keys and nesting explain both?
answer
- same component means no enter or leave
- a key forces a new instance
- the slot route is the whole route
- key each level on what it owns
- per-route names from meta
basics
~20 sThe same component is reused between messages, so Transition has nothing to animate until a key forces a remount. A fullPath key on the outer view remounts the layout on every child switch; key each level on what it owns.
solid answer
~40 s`/mail/inbox/1` and `/mail/inbox/2` render the same component in the same view, so Vue patches it and `<Transition>` has no enter or leave to animate. The docs' fix is `<component :is="Component" :key="route.path" />` in the slot, which gives each URL its own instance. The catch is that the slot's `route` is the **whole current route** at every depth. A `:key="route.fullPath"` on the top-level view changes whenever any child, param or query changes, so `SettingsLayout` is destroyed and remounted on every tab switch, and the sidebar loses its state. Key each level on what it owns: the pane that shows one message on `route.params.messageId`, the outer layout on nothing. Per-route transition names come from `route.meta.transition` in the same slot. Keys inside `KeepAlive` also multiply cache entries.
code
vue · 8 lines<!-- App.vue: no key, the settings layout survives child switches -->
<template>
<RouterView v-slot="{ Component, route }">
<Transition :name="route.meta.transition || 'fade'" mode="out-in">
<component :is="Component" />
</Transition>
</RouterView>
</template>go deeper
Remember that a transition only plays when the component changes, and a key can force that change.
Explain patch versus remount, what the slot's route contains, and per-route transition names from meta.
Place keys per level on what each view owns, avoid fullPath keys on layouts, and bound KeepAlive caches when views are keyed.
Define where the app accepts remounts and where it guarantees persistence, and encode it in shared layout components.
## Two symptoms, one mechanism Vue decides between **patching** and **remounting** by comparing the component type and the `key` of the vnode it renders. `RouterView` itself adds no key. So: - if a navigation leaves the same component at a view's depth, the instance is **reused**; - if the component changes, or you give it a new `key`, the old instance **unmounts** and a new one **mounts**. `<Transition>` animates only enter and leave, so a reused instance plays nothing. ## Symptom 1: no fade between messages On `/mail/inbox/1` to `/mail/inbox/2`, the detail view renders `MessageDetail` both times. Vue patches it, the text changes in place, and the fade never runs. The router docs' fix is a key in the slot: ```vue <RouterView v-slot="{ Component, route }"> <Transition name="fade" mode="out-in"> <component :is="Component" :key="route.path" /> </Transition> </RouterView> ``` Now each path gets a distinct vnode key, so the old message leaves and the new one enters. The price is a full remount per message: local state resets and `setup` runs again. ## Symptom 2: the settings sidebar resets The same idea applied at the **top level** breaks the layout. The slot's `route` is the complete current route, the same object at every depth. On `App.vue`'s view: 1. `/settings/profile` to `/settings/security` changes `route.fullPath`. 2. The key on depth 0 changes, so Vue remounts `SettingsLayout`, even though the parent record did not change. 3. The sidebar, rendered by the layout, loses its scroll, expanded groups and any data loaded in `setup`. A key bound to `$route.fullPath` directly on a `<RouterView>` element has the same effect on everything under it. ## Choosing keys per level | View | Key | Remounts when | |---|---|---| | top level in `App.vue` | none | the top-level component changes | | mail detail pane | `route.params.messageId` | another message opens | | settings pane | none, or `route.path` | the section changes | | any level | `route.fullPath` | anything changes, including query and hash | The rule: **key a view on what that view displays**. `fullPath` includes query and hash, so it also remounts on a sort change or an anchor jump, which is almost never wanted. ## Per-route transitions The same slot can choose a transition per route: ```vue <RouterView v-slot="{ Component, route }"> <Transition :name="route.meta.transition || 'fade'"> <component :is="Component" /> </Transition> </RouterView> ``` - Each record declares `meta: { transition: 'slide-left' }`; the view falls back to `fade`. - A global `afterEach` hook can instead compute the name from the depth of `to` and `from`, sliding left when going deeper and right when going back up, as the router guide shows. - `route.meta` merges the meta of every matched record, so a parent's transition name applies to its children unless a child overrides it. ## Keys and KeepAlive `KeepAlive` caches by vnode key when there is one, else by component. Keying a cached view on `route.path` therefore creates **one cache entry per URL**: every message opened stays in memory until evicted. Bound it with `KeepAlive`'s `max`, key on something coarser, or skip caching for per-item panes. ## What the user feels either way | Behaviour | Reuse, no key change | Remount, new key | |---|---|---| | Transition | none | enter and leave play | | Local state | kept | reset | | Data loading | must watch params | runs in `setup` again | | Scroll inside the view | kept | reset | | Focus inside the view | kept | lost | | Cost | a patch | a full unmount and mount | Neither is wrong. A message pane benefits from a remount: fresh state per message and a visible transition. A settings layout benefits from reuse: the sidebar should never flicker. The skill is choosing per level. ## A debugging routine 1. For each view, write down which component it renders before and after the navigation. 2. Same component and no key change: expect reuse, no transition, no `onMounted`. 3. Different component or changed key: expect unmount and mount, a transition, fresh local state. 4. If the result surprises you, look for a key using `route` at a level that does not own that part of the URL.
- Why is route.fullPath usually the wrong key even for a leaf view?`fullPath` includes the query and hash, so changing a sort order, a filter or jumping to an anchor remounts the view and replays its transition. Key on the params that identify what the view shows, such as `messageId`, or on `route.path` when the query is irrelevant to it.
- The team wants a slide-left animation when drilling into a message and slide-right when going back. Where does that logic go?Compute the name per navigation and read it in the slot. The router guide uses a global `afterEach` that compares the depth of `to.path` and `from.path` and writes `to.meta.transition`; the view binds `<Transition :name="route.meta.transition">`. Keep the key on the pane that changes so the animation has an enter and a leave.
saying these in an interview costs you the question
- Transition always animates route changes, even when the component is reused.
- The slot's route is only the part of the route this level matched.
- Keying the top-level view on fullPath is harmless for nested layouts.
- A key inside KeepAlive has no effect on how many instances are cached.
- Vue Router adds a key to RouterView's component automatically.