AcademyVue
Performance Patterns: KeepAlive & v-memo
KeepAlive caches a component instance instead of destroying it; v-memo skips a re-render entirely when nothing it depends on changed.
2 min readUpdated 3 exercises
v-if between components destroys whichever one you switch away from
<template>
<Profile v-if="tab === 'profile'" />
<Settings v-else />Switching tab does not hide the inactive component — it unmounts it entirely. Any local state Settings was holding (an unsaved draft, a scroll position, an expanded section) is gone the moment tab changes away from it, and switching back mounts a brand-new instance from scratch.
KeepAlive caches the instance instead of destroying it
<template>
<KeepAlive>
<Profile v-if="tab === 'profile'" />
<Settings v-else />
</KeepAlive>
</template>Wrapped in <KeepAlive>, switching away detaches the inactive component instead of destroying it — its state, DOM and all, held in memory — and switching back reattaches the exact same instance, state intact. Two additional lifecycle hooks exist specifically for this: onActivated and onDeactivated fire on the cached component each time it is reattached or detached, distinct from onMounted/onUnmounted, which for a kept-alive component only fire once each, on its very first mount and its eventual real destruction (when it is no longer even a possible branch, not just temporarily inactive).
<KeepAlive> trades memory for state preservation — every cached instance stays alive, so it is a deliberate choice for views genuinely worth preserving (a tab with a draft, a multi-step wizard), not a default to wrap everything in.
v-memo skips re-rendering a subtree when nothing in its list changed
<li v-for="item in todos" :key="item.id" v-memo="[item.id, item.done]">
{{ item.done ? 'Done' : 'Pending' }}
</li>Before patching this <li>, Vue compares the current [item.id, item.done] against the values from the last render. If both are unchanged, it skips re-rendering that entire subtree — a real optimization for large lists where most rows are unaffected by whatever caused the parent to re-render.
The array is a list of every value the memoized subtree's output actually depends on — not a list of "things that identify this row." v-memo="[item.id]" alone, on a row whose text depends on item.done, means Vue happily skips a render where item.done changed, because the one value in the array (item.id) never did. The bug is invisible in the exact same way as an under-specified watch source list: the code runs, nothing throws, and the specific update that should have happened silently does not.
Both are opt-in, and both cost something to use wrong
Neither <KeepAlive> nor v-memo is free: <KeepAlive> holds memory for every cached instance, and a wrong v-memo array does not just fail to optimize — it actively suppresses correct updates. Reach for either once a specific, measured cost justifies it, not as a default habit on every list or every conditional component.
What interviewers check
Whether a v-memo array gets audited for completeness, not just presence — "is everything the template reads inside there?" is the one question that catches this bug on sight, and it is the same question watchEffect exists to make automatic elsewhere.
Now practice it
1 quiz · 2 drillsReading this page is the cheap half. These are the exercises that make you use it.
- KeepAliveProA tab switcher that resets an unsaved draft every time you switch away and back — fixed by caching, not destroying.DrillSolve · HardDrillSolve · Hard
- Render Performance (v-memo)ProA v-memo dependency array missing item.done, so a real toggle silently fails to re-render.DrillSolve · HardDrillSolve · Hard
- Performance · v-memo & v-oncev-memo and v-once are exactly this set's subject — the fastest way to check the concept actually stuck.QuizTake · EasyQuizTake · Easy
Read next
Teleport & Suspense
Teleport moves a component's DOM without moving its logical position; Suspense gives an async component a fallback while it loads.
2 min
Computed Properties: Cached, Derived State
A computed only recalculates when a dependency it read actually changes — the caching a plain method call can never give you.
3 min