I'm working with a component that was built out called 'Stepper' it's a basic component that has the steps at the top to guide the user through a process. In this stepper we have a dynamic number of slots that are produced and we pass a component in to fill that slot.
Stepper.vue
...
<div v-for="step in steps" :key="step.name">
<div v-if="step.name === currentItem.name">
<slot :name="`${step.name}`" />
</div>
</div>
...
Parent.vue
...
<Stepper :items="steps"
<template v-for="step in steps" v-slot:[step.name] :key="step.name">
<keep-alive>
<component :is="step.component" />
</keep-alive>
</template>
</Stepper>
...
<script lang="ts">
...
import Stepper from "@/components/Stepper.vue";
import Component1 from "@/components/Component1.vue";
import Component2 from "@/components/Component2.vue";
export default defineComponent({
name: "ParentComponent",
components: {
Stepper,
Component1,
Component2,
},
setup() {
const steps = ref([
{
name: "step1",
label: "Step 1",
component: "Component1",
},
{
name: "step2",
label: "Step 2",
component: "Component2",
},
]);
return {
steps,
}
}
</script>
All in all everything is working. The component for each step displays, the steps increment so on and so forth. The issue we're having is that the <keep-alive>
is not working. Every time you go back or forth in the steps the component re-renders and we're seeing the onMounted
hook called. Granted we can pre-populate the form with some 'saved' data if needed but would be nice if we could get the <keep-alive>
to work. We've also tried adding the :include="[COMPONENT_NAMES]"
to the <keep-alive>
as well, to no avail.
Edit: Updated code snippet with more details.
CodePudding user response:
Make sure your not testing this in your development environment that usually has Chaches disabled. Therefore if you don't have a cached reference the keep-alive wont work.
This means that Vue does not have to create a new instance every single time you switch components. **Instead, it just uses the cached reference whenever you come back to it**. Keep-Alive is what VueJS calls an abstract element – meaning that it does not render a DOM element nor does it show up as a component.
Also you could have the cache disabled in the Console tools.
To be certain you can check this setting:
#another edit:
I have done some more research, and adding a key to your Keep-alive component
<component :is="step.component" :key="UniqueIdentifier" />
will help your vue-router understand that this component is changed.
Check out this other SA answer: vue Keep-alive not working
CodePudding user response:
The <keep-alive>
is caching the dynamic component
, but the v-if
directive inside Stepper.vue
removes the div.stepper-pane
and its <slot>
(thus destroying the current component before mounting the new one) based on currentItem.name
, which leads to the behavior you're seeing.
One way to fix this is to refactor Stepper.vue
, moving the list rendering into the default slot. To me, this makes sense because Stepper.vue
only ever renders one step/slot at a time.
<!-- ParentComponent.vue -->
<Stepper :currentItem="currentItem">
<keep-alive>
<component :is="currentItem.component" />
</keep-alive>
</Stepper>
<!-- Stepper.vue -->
<div >
<slot />
</div>