In Vue 3, why can `<KeepAlive include="CheckoutForm">` fail to cache a component, and how does Vue match `include` and `exclude`?
answer
- matching is not by import
- the component's name option
- script setup infers from filename
- comma strings are split, not trimmed
basics
~20 sVue's KeepAlive matches include and exclude against the component's name option, not its import or tag. A component with no matching name, a misspelled filename-inferred name, or a spaced comma list is simply not cached.
solid answer
~40 s`include` and `exclude` accept a comma-delimited string, a `RegExp`, or an array of either, and are matched against the child component's **name**. For a `<script setup>` SFC the name is inferred from the filename (since 3.2.34), so `CheckoutForm.vue` matches `"CheckoutForm"`; an explicit name can be set with `defineOptions({ name })` (3.3+) or the `name` option. Common failures: the file is called `checkout-form.vue`, the component is anonymous, or the list is written `"CartView, CheckoutForm"` with a space, because the string is split on commas and matched exactly. RegExp and array values need `v-bind`. When `include` or `exclude` changes at runtime, cached instances that no longer qualify are destroyed.
code
vue · 7 lines<script setup lang="ts">
defineOptions({ name: 'CheckoutForm' })
</script>
<template>
<form>...</form>
</template>go deeper
Know that include and exclude choose which components KeepAlive caches, and that they refer to component names.
Explain where the name comes from (explicit name, defineOptions, filename inference for script setup) and the exact string, RegExp and array matching rules.
Debug the silent non-caching cases quickly and use a reactive include list to invalidate specific cached views after their data goes stale.
Set a convention for explicit component names on anything cached, so refactors and file renames cannot quietly change caching behaviour.
## What `include` and `exclude` do By default `<KeepAlive>` caches **every** component instance that passes through it. Two props narrow that down: - `include`: only components whose name matches are cached; everything else is unmounted normally on switch-away; - `exclude`: components whose name matches are never cached; everything else is. Both take the same pattern type: a **comma-delimited string**, a **`RegExp`**, or an **array** containing strings and regular expressions. ```vue <template> <KeepAlive include="CartView,CheckoutForm"> <component :is="step" /> </KeepAlive> <KeepAlive :exclude="/^Preview/"> <component :is="panel" /> </KeepAlive> <KeepAlive :include="['CartView', /Form$/]"> <component :is="step" /> </KeepAlive> </template> ``` A plain attribute is always a string, so the RegExp and array forms must be bound with `v-bind` (`:include`). ## What the pattern is matched against The match is against the component's **name**, not the variable it was imported as and not the tag used in the template. Vue resolves the name in this order: 1. an explicit `name`, set with `defineOptions({ name: 'CheckoutForm' })` in `<script setup>` (Vue 3.3+) or with the `name` option in a normal `<script>` block; 2. for a `<script setup>` SFC with no explicit name, the name **inferred from the filename** (Vue 3.2.34+), so `CheckoutForm.vue` becomes `CheckoutForm`. For an async component created with `defineAsyncComponent`, the name checked is the one of the loaded inner component once it has resolved. ## How the string form is compared The string form is split on commas and each piece is compared to the name **exactly**. There is no trimming and no case folding: | `include` value | Name `CheckoutForm` cached? | Why | |---|---|---| | `"CartView,CheckoutForm"` | Yes | Exact piece after split | | `"CartView, CheckoutForm"` | No | The piece is `" CheckoutForm"` with a leading space | | `"checkoutform"` | No | Comparison is case-sensitive | | `"checkout-form"` | No | No kebab-to-Pascal conversion is applied | | `:include="/Form$/"` | Yes | RegExp tested against the name | ## Why a component silently is not cached When something "is not being cached", check in this order: - **No name at all.** With `include` set, a component without a resolvable name never matches, so it is not cached. Anonymous components from inline objects or render functions are typical. - **The file name is not the name you think.** `checkout-form.vue` infers `checkout-form`, which does not match `CheckoutForm`. - **A space in the comma list**, as in the table above. - **A RegExp or array written without `v-bind`**, which turns it into a literal string. - **`exclude` wins where both match**: a component matching `exclude` is not cached even if `include` also matches. The mirror case for `exclude`: a component with no name can never match `exclude`, so it **is** cached. ## Changing the rules at runtime `include` and `exclude` are reactive. `<KeepAlive>` watches them and, after the next render, prunes cached entries whose names no longer qualify; those instances are destroyed with their normal unmount hooks. That is a practical way to **invalidate** a cache: remove a name from `include` when its data becomes stale, then add it back. ```ts import { ref } from 'vue' export const cachedViews = ref<string[]>(['CartView', 'CheckoutForm']) export function dropFromCache(name: string) { cachedViews.value = cachedViews.value.filter((n) => n !== name) } ``` Binding `:include="cachedViews"` and calling `dropFromCache('CheckoutForm')` after a successful order destroys the cached form, so the next visit starts clean. ## Debugging checklist When a view is not being cached as expected, confirm the facts in the browser rather than guessing: 1. Open Vue Devtools and read the component's displayed name; that is close to what `include` will be compared with. 2. Log the bound `include` value and check for spaces, casing and kebab-case versus PascalCase. 3. If the list is dynamic, confirm it contains the name **at the moment of switching away**: a name added after the switch cannot bring back an instance that was already unmounted. 4. Check whether the same name also appears in `exclude`. Fixing these usually means adding an explicit `defineOptions({ name })` and switching to an array value, which is both typed and whitespace-proof. ## Summary KeepAlive filters by **component name**. Make names explicit or rely on filename inference deliberately, write comma lists without spaces or use arrays, and bind RegExp or array values with `v-bind`. Treat the reactive `include` list as the tool for evicting a specific cached view.
- What happens to already cached instances when you remove a name from a bound `include` array?`<KeepAlive>` watches `include` and `exclude` and, after the next render, prunes cached entries whose names no longer match. Those instances are destroyed with their normal unmount hooks, so the next visit builds a fresh instance. That makes a reactive `include` list a clean way to invalidate one cached view.
- A component matches both `include` and `exclude`; is it cached?No. Vue skips caching when `include` is set and the name does not match, or when `exclude` is set and the name matches. So a name present in `exclude` is never cached, whatever `include` says.
saying these in an interview costs you the question
- include matches the tag name you wrote in the template
- include matches the variable name the component was imported as
- Spaces after commas in include="A, B" are ignored
- A regular expression works as a plain include attribute without v-bind
- Changing include at runtime has no effect on instances already cached