Vue 2 <keep-alive> not working with <router-view> and key

17,250

Solution 1

For use keep-alive with router view you should use unique key on view router like this:

 <keep-alive>
   <router-view :key="$route.fullPath"></router-view> 
 </keep-alive>

Dont forgot use max attribute on keep alive to prevent memory overhead

<keep-alive max="5">

or just include components want cache:

<keep-alive include="FirstComp,SecondComp"> 

And for each components inside component you need keep alive them if you want cache

<keep-alive>
  <product></product>
</keep-alive>

Solution 2

Vue 3 - working example

When I tried solutions from above (in Vue 3) I got this in the console

[Vue Router warn]: <router-view> can no longer be used directly inside <transition> or <keep-alive>.
Use slot props instead:

<router-view v-slot="{ Component }">
  <keep-alive>
    <component :is="Component" />
  </keep-alive>
</router-view>

So I copied it into the App.vue like this...

// App.vue
...
  <router-view v-slot="{ Component }">
    <keep-alive>
      <component :is="Component" />
    </keep-alive>
  </router-view>
...

And it works :)

Solution 3

I don't think you will be able to cache router-view using keep-alive. From the documentation of keep-alive:

keep-alive does not work with functional components because they do not have instances to be cached.

And router-view is a functional component, from the documentation:

The component is a functional component that renders the matched component for the given path.

To make this work, you need to have dynamic component inside keep-alive, like following:

<keep-alive>
  <products ></products>
</keep-alive>

Solution 4

Have a look from what I posted here for vue 2+:

vue.js keep-alive and router-view

If you want to swap things around based on the route url you can add children to your routes.js file and bind off parameters that way. For example if you wanted to have your component kept alive but need the data to change from part of the path you can do something like this:

{ 
  path: 'products', component: Products, ...,
  children: [
              { path: 'Overview/:name', props: true, component: Overview },
              { path: 'Overview/:name/:id', props: true, component: Details }
            ]
}

You can route first to /Products, then on say user click of an image or list of whatever you want route to /Products/Overview/Memes, after the user then clicks a specific meme you could route to /Products/Overview/Memes/MrPotatoHead and have the component load data on Mr Potato Head or something to that effect.

Its important to note that the Products component will be kept alive but the sub components will not. Even if you wrap the sub components template in keep-alive it will be destroyed:

destroy nested keep-alive components

Solution 5

Just because your component is re-mounted and therefore fires your function on the mounted hook doesn't mean it was ever destroyed. To test if it was kept alive, console log something on the destroyed hook, and if it doesn't fire the console log, the keep-alive is working. I've run your same code, and it seems to work in Vue 2 at least.

Share:
17,250
okwme
Author by

okwme

Updated on July 27, 2022

Comments

  • okwme
    okwme almost 2 years

    I'm using vue-router with a series of components like tabs. Each <router-link> is a tab and the space below is the <router-view>. Two of the tabs are the same component with different filters, let's say they are products and the router adds a parameter for filtering: /products/new & /products/sale.

    Inside of products are individual product components which get mounted when the route is opened. My problem is that when I switch between the routes, and the filter parameter is changed, the product components get remounted every time. I'd like to cache them so switching back and forth is easier. To do this I set up <keep-alive> and added :key='$route.fullPath' to my <router-view> but they don't seem to be cached. Each product is still firing a mounted() event when i switch between products.

    <keep-alive>
      <router-view :key='$route.fullPath'></router-view>
    </keep-alive>
    

    Should I make each products view into a separate component?

  • okwme
    okwme over 7 years
    Hi thanks for pointing that out! The first solution that came to mind was to remove <router-view> and put back my dynamic components like: <keep-alive> <component v-bind:is="$route.name"/> </keep-alive>where the name of the route would be the name of the component. This works in terms of routing but the component is still being remounted when clicking between the two versions : \
  • oliverpool
    oliverpool about 6 years
    Doesn't seem to be true anymore. Quoting the router documentation: Since it's just a component, it works with <transition> and <keep-alive>. When using the both together, make sure to use <keep-alive> inside
  • pishpish
    pishpish almost 4 years
    @oliverpool You can quote all you want but that documentation example does not work.
  • Osvaldo Maria
    Osvaldo Maria about 3 years
    Note: <keep-alive include="FirstComp,SecondComp"> will only work for named components(components with a 'name' property).
  • McMuellermilch
    McMuellermilch about 2 years
    @OsvaldoMaria And what about child-routes? I am having the issue that only top-level routes are being cashed. The child routes are not. I am passing the names of the components correctly and have the name property in said components as well.
  • Sibyl
    Sibyl about 2 years
    @pishpish Please stop with the misinformation if you can't make it work.