Skip to the solution
MediumFrontend9 min read

What is keep-alive in Vue?

576 views

The problem

Question presented to candidate: "Users complain that when they go from the inbox to a draft and back, the inbox loses its scroll position and the filter they typed. Someone suggested wrapping the view in <KeepAlive>. What does that actually do, and what would you watch out for?"

What a strong answer should cover:

  • <KeepAlive> wraps a dynamic child and caches the component instance when it is switched away, instead of unmounting it; its state and DOM are kept off-screen.
  • Cached components get onActivated / onDeactivated instead of mount and unmount on each switch; onActivated also runs on the first mount.
  • include / exclude match the component's name option (inferred from the file name for <script setup> SFCs); max caps the cache and evicts the least recently used instance.
  • It needs exactly one direct component child, typically <component :is> or the slot of <RouterView>.
  • Cached instances keep their watchers, timers and memory alive, so cleanup and refetching belong in the activation hooks.

Clarifying questions expected:

  • "Should every view be cached, or only the expensive ones?"
  • "When a cached view comes back, should it refresh its data or show the old snapshot?"

Code / implementation expected: Yes β€” a dynamic tab set inside <KeepAlive> with include/exclude or max, and the activation hooks.

  • keep-alive

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

9 min read Β· 10 sections

Read the code

VueKeepAlive with max and exclude, logging every lifecycle hook
<script setup>
import { ref, shallowRef, defineComponent, h, onMounted, onUnmounted, onActivated, onDeactivated } from "vue";

// A factory so each tab is its own named component with its own counter.
const makeTab = (name) =>
  defineComponent({
    name, // <KeepAlive include/exclude> matches on this name
    setup() {
      const count = ref(0);
      onMounted(() => console.log(`  ${name}: mounted`));
      onActivated(() => console.log(`  ${name}: activated`));
      onDeactivated(() => console.log(`  ${name}: deactivated`));
      onUnmounted(() => console.log(`  ${name}: unmounted`));
      return () => h("button", { class: "inc", onClick: () => count.value++ }, `${name} count ${count.value}`);
    },
  });

const tabs = { Inbox: makeTab("Inbox"), Drafts: makeTab("Drafts"), Search: makeTab("Search") };
const current = shallowRef(tabs.Inbox);
</script>

<template>
  <nav>
    <button v-for="(comp, name) in tabs" :key="name" class="tab" @click="current = comp">{{ name }}</button>
  </nav>
  <!-- Cache at most 2 instances, and never cache Search -->
  <KeepAlive :max="2" exclude="Search">
    <component :is="current" />
  </KeepAlive>
</template>

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 46 of 92 in Vue.js

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Vue.js questions
Up nextMedium

What is useTemplateRef() (Vue 3.5) and how does it improve on ref=""?

Continue