Home > Net >  Vue.js <keep-alive> in template for slot not working
Vue.js <keep-alive> in template for slot not working

Time:02-10

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:

enter image description here

#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>

demo

  • Related