Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

keepalive include/exclude props don't work with <NuxtPage> #15214

Closed
Ilmion opened this issue Oct 20, 2022 · 72 comments · Fixed by #24024
Closed

keepalive include/exclude props don't work with <NuxtPage> #15214

Ilmion opened this issue Oct 20, 2022 · 72 comments · Fixed by #24024

Comments

@Ilmion
Copy link

Ilmion commented Oct 20, 2022

Environment


  • Operating System: Windows_NT
  • Node Version: v16.16.0
  • Nuxt Version: 3.0.0-rc.11
  • Nitro Version: 0.5.4
  • Package Manager: npm@8.11.0
  • Builder: vite
  • User Config: -
  • Runtime Modules: -
  • Build Modules: -

Reproduction

https://stackblitz.com/edit/github-xnrjjn?file=pages%2Findex.vue,pages%2Fnotkeptalive.vue

Describe the bug

App has 2 pages, one page is set to be kept alive with
definePageMeta({ keepalive: true });
the other one is not.

Changing some value in the first page, is not kept if you navigate away from it and if you come back.
Also, there is a weird display bug, as if a new mounted component of the page was added under the first one in the page before the first one being added. It make the page flicker.

If you put the same page meta in the second one (by removing the comment is the reproduction link), now it works, but the two pages are now kept alive and this is not what I want.

In base vue3, I could do something like that :

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

Additional context

No response

Logs

No response

Copy link
Member

For this use case, you can pass keep alive props directly to NuxtPage to get the kind of behaviour that you want.

@danielroe danielroe closed this as not planned Won't fix, can't repro, duplicate, stale Oct 20, 2022
@Ilmion
Copy link
Author

Ilmion commented Oct 20, 2022

For this use case, you can pass keep alive props directly to NuxtPage to get the kind of behaviour that you want.

I cannot find an example in the documentation, could you demonstrate ?

Copy link
Member

Feature implemented here: nuxt/framework#7492.

You would do something like:

<NuxtPage :keepalive="{ include: 'index' }" />

@IsraelOrtuno
Copy link
Contributor

IsraelOrtuno commented Oct 21, 2022

I am with @Ilmion <KeepAlive> does not seem to work when not used in a <NuxtPage> tag.

The only way I could make it work was like this in my app.vue:

<NuxtPage :keepalive="{}" />

Coudn't make it in a per page basis with definePageMeta({ keepalive: true })

Or is not clear how this should work (https://github.com/nuxt/framework/discussions/8366)

All these options wraps the page in a <KeepAlive> btw.

Copy link
Member

Using keepalive in definePageMeta does work. But it's mostly useful for situations where you have a parent page which wants to define keepalive settings for its children. It will never work for the lone page that wants to be kept alive, because keepalive settings need to remain the same in order for a route to be kept alive.

@IsraelOrtuno
Copy link
Contributor

That makes more sense now. Thanks for clarifying.

@toniengelhardt
Copy link
Contributor

@danielroe

According to your explanation I came up with the following, but neither index nor IdeaList are kept alive. When I navigate from ideas/index.vue to an item in the list and go back, both index and IdeaList onMount() are executed.

Moving the keepalive for IdeaList to ideas/index.vue also doesn't work.

/pages
|- /ideas
    |- /[id]
        |- index.vue
    |- index.vue
|- ideas.vue

ideas.vue

<template>
  <div class="content">
    <NuxtPage />
  </div>
</template>

<script setup lang="ts">
definePageMeta({
  keepalive: {
    include: ['index', 'IdeaList']
  }
})
</script>

ideas/index.vue

<template>
  <div>
    <IdeaList :items="items" />
  </div>
</template>

ideas/[id]/index.vue

<template>
  <div>
    IdeaListItem content
  </div>
</template>

@Ilmion
Copy link
Author

Ilmion commented Oct 21, 2022

Feature implemented here: nuxt/framework#7492.

You would do something like:

<NuxtPage :keepalive="{ include: 'index' }" />

@danielroe It does not work. I have inspected it with chrome vue dev tool and here is the problem.
Under the KeepAlive there is a Anonymous element created around the Index element and because is it not exactly under the KeepAlive the include does not work.
When the keepalive is setup globaly, it is the Anonymous object that are kept alive, not the one from the ./pages.

Two joint picture from the Chrome dev tool.
With the include in the, ie
<NuxtPage :keepalive="{ include : 'index' }">
KeepAliveWithInclude
With the keepalive set globaly, you can see another Anonymous under the KeepAlive
KeepAliveWithoutInclude

This bug isn't closed IMHO.

@danielroe danielroe reopened this Oct 21, 2022
@danielroe danielroe changed the title Keepalive not working when not applied to every pages keepalive include/exclude props don't work with <NuxtPage> Oct 21, 2022
@stenet
Copy link
Contributor

stenet commented Nov 26, 2022

@danielroe any plans to fix this in the near future?

I did some testing and it looks like the reason for this problem is that the direct child of KeepAlive is an anonymous component (see last comment on #14609), that has no name and for this reason Vue include/exclude does not work.

The anonymous component also is the reason for #15573. As the component type below KeepAlive is always the same Vue unmounts the wrong component, resulting in a strange state. This causes KeepAlive to be unsafe to use in Nuxt at the moment, as components/composables that register onUnmounted get this hook even though the component is not unmounted.

In my test, I changed the behavior so that each pageKey create its own wrapper component https://github.com/stenet/nuxt-framework-keep-alive/blob/main/packages/nuxt/src/pages/runtime/page.ts#L88. This way, these two problems were fixed. But this is probably not the best solution, since the map used grows quite fast.

Copy link
Member

Yes, this is definitely on the roadmap and needs to be fixed.

@vcasy
Copy link

vcasy commented Dec 8, 2022

How can nuxt be stable with major bugs like this 😢
I don't want to be rude (nuxt is great!) - just questioning if I should use nuxt3 for any upcoming project.

@kikuchan
Copy link

Is there any workaround for this?

@danielroe danielroe added the 3.x label Jan 19, 2023
@danielroe danielroe transferred this issue from nuxt/framework Jan 19, 2023
@vcasy
Copy link

vcasy commented Feb 1, 2023

I hope this gets fixed sometime.
Just checked with newest release v3.1.1 - bug still remains (same reason described by Ilmion).

Workaround: Use <RouterView /> instead <NuxtPage />

If you use this solution you also have to manully import useRoute everytime:
import { useRoute } from 'vue-router';

Because the auto-imported useRoute of nuxt will be wrong

@AndrewBogdanovTSS
Copy link

@danielroe any updates on this one? Thanks

@stephenjason89
Copy link

stephenjason89 commented Mar 16, 2023

@danielroe Doing nuxt-page inside layout/default.vue instead of slot like so
image

Is not working as it should be. It is keeping alive the component but not excluding the excludeThis component.
Because nuxt is creating another element in between the keepAlive and excludeThis component
image

This was working on nuxt2 and nuxt-bridge with this syntax
image

Is there a way to do this properly?
Thank you so much

EDIT: @vcasy I tried RouterView instead and keepAlive completely stopped working
image

@ajh99990
Copy link

In the latest version v3.3.1, this bug still exists.

@kikuchan
Copy link

After some investigation, I've noticed that the following rules:

  • KeepAlive only looks at the name of the component for include/exclude
  • A page component wrapped by NuxtPage, is also wrapped by RouteProvider named RouteProvider

That's why the include/exclude keepalive options don't work for NuxtPage.

There are some solutions worth to consider I've found so far;

  1. Make KeepAlive looks at key in addition to name, in Vue3
  2. Give each RouteProvider component a distinct name.
  3. Remove RouteProvider and provide _route reactive somehow.

I've also came up with a PoC code for 2nd solution, but bit hacky;

--- packages/nuxt/src/pages/runtime/page.ts
+++ packages/nuxt/src/pages/runtime/page.ts
@@ -36,6 +36,8 @@ export default defineComponent({
   },
   setup (props, { attrs }) {
     const nuxtApp = useNuxtApp()
+    const RouteProvider = defineComponent({ ...RouteProviderTemplate })
+
     return () => {
       return h(RouterView, { name: props.name, route: props.route, ...attrs }, {
         default: (routeProps: RouterViewSlotProps) => {
@@ -44,6 +46,8 @@ export default defineComponent({
           const key = generateRouteKey(routeProps, props.pageKey)
           const done = nuxtApp.deferHydration()

+          RouteProvider.name = String(key)
+
           const hasTransition = !!(props.transition ?? routeProps.route.meta.pageTransition ?? defaultPageTransition)
           const transitionProps = hasTransition && _mergeTransitionProps([
             props.transition,
@@ -77,12 +81,11 @@ function _mergeTransitionProps (routeProps: TransitionProps[]): TransitionProps
   return defu(..._props)
 }

-const RouteProvider = defineComponent({
-  name: 'RouteProvider',
+const RouteProviderTemplate = ({
   // TODO: Type props
   // eslint-disable-next-line vue/require-prop-types
   props: ['routeProps', 'pageKey', 'hasTransition'],
-  setup (props) {
+  setup (props: any) {
     // Prevent reactivity when the page will be rerendered in a different suspense fork
     // eslint-disable-next-line vue/no-setup-props-destructure
     const previousKey = props.pageKey

@geauser
Copy link

geauser commented Aug 24, 2023

I know the issue is being worked on by the team of nuxt, but in the mean time, does anyone have a comprehensible guide for a workaround? I don't tried this but it does not work: #15214 (comment)

@mkarras
Copy link

mkarras commented Aug 24, 2023

I know the issue is being worked on by the team of nuxt, but in the mean time, does anyone have a comprehensible guide for a workaround? I don't tried this but it does not work: #15214 (comment)

I think you need to disable keep-alive completely to for example not run into security issues from cached data that should not be cached. Enable keep-alive back with the excluded routes when this issue is fixed.

When using the router directly, there are issues with the Nuxt ecosystem.

@henriquevschroeder

This comment was marked as duplicate.

@11003
Copy link

11003 commented Sep 7, 2023

+1

@gjssss
Copy link
Contributor

gjssss commented Sep 11, 2023

I tried it out and found that the route-provider component is indeed preventing the keep-alive from matching components by name, which is causing the functionality to fail. I've come up with two possible solutions:

  1. Dynamically change the name of the route-provider component. I think it might be reasonable to change the component name to something based on the included page name, such as IndexProvider. However, changing the component name at runtime may not be ideal, but given the current issue, it could serve as a temporary solution.

  2. Add support for Vue 3 keep-alive to match components based on a key. This solution may require intervention from the Vue team. However, it seems that this issue originated from the design of Nuxt, and waiting for Vue to address it might take some time, which could be a loss for Nuxt. I've attempted to use some Hacking methods to add components to the Cache Map of the keep-alive component but haven't succeeded.

  3. Is it possible to inject the provider into the user's page in some way, automatically wrapping the user's code, and ensuring that keep-alive uses the page component for matching (I haven't tried this, and I'm not sure if it's feasible)?

In summary, I hope that Nuxt can provide some temporary solutions for this issue (assuming that the API won't change) and include warnings or notifications about this problem in the documentation to help those who are facing similar problems.🌹

@AndrewBogdanovTSS
Copy link

@gjssss from my conversations with Nuxt team I think they are expecting a fix in Vue 3 first

@gjssss
Copy link
Contributor

gjssss commented Sep 11, 2023

@AndrewBogdanovTSS Indeed, I think that at least for now, some temporary solutions can be used to address this issue, or the problem can be highlighted in the Nuxt documentation.

@AndrewBogdanovTSS
Copy link

@gjssss absolutely agree

@karlsbeard
Copy link

I tried it out and found that the route-provider component is indeed preventing the keep-alive from matching components by name, which is causing the functionality to fail. I've come up with two possible solutions:

  1. Dynamically change the name of the route-provider component. I think it might be reasonable to change the component name to something based on the included page name, such as IndexProvider. However, changing the component name at runtime may not be ideal, but given the current issue, it could serve as a temporary solution.
  2. Add support for Vue 3 keep-alive to match components based on a key. This solution may require intervention from the Vue team. However, it seems that this issue originated from the design of Nuxt, and waiting for Vue to address it might take some time, which could be a loss for Nuxt. I've attempted to use some Hacking methods to add components to the Cache Map of the keep-alive component but haven't succeeded.
  3. Is it possible to inject the provider into the user's page in some way, automatically wrapping the user's code, and ensuring that keep-alive uses the page component for matching (I haven't tried this, and I'm not sure if it's feasible)?

In summary, I hope that Nuxt can provide some temporary solutions for this issue (assuming that the API won't change) and include warnings or notifications about this problem in the documentation to help those who are facing similar problems.🌹

nice shot

@sanba-anass
Copy link

setting app : {keepalive:true} in the nuxt config file and disabling each page i don't want to keep alive with definePageMeta({ keepalive: false }) works for me ,is there any downside using this method thanks in advance!

@citrusjunoss
Copy link

There is an urgent need for someone to provide a temporary solution so that the problem can be addressed as soon as possible

@huoyou
Copy link

huoyou commented Oct 20, 2023 via email

@mkarras
Copy link

mkarras commented Oct 20, 2023

setting app : {keepalive:true} in the nuxt config file and disabling each page i don't want to keep alive with definePageMeta({ keepalive: false }) works for me ,is there any downside using this method thanks in advance!

I think someone here already tried. The first time when a non keep-alive page is visited,the whole cache is cleared. Making the feature more or less useless.

@stenet
Copy link
Contributor

stenet commented Oct 21, 2023

@danielroe @bencodezen could you please add a reference to the related vue issue here? I tried to find it but I'm not sure which one it is.

Thanks!

@Maxbsy
Copy link

Maxbsy commented Nov 14, 2023

Any idea when this will be fixed? Or can you tell us about the fix plan for this problem? Is there any hope of solving this problem in the short term? If not, can you provide a temporary solution?

@kr2748
Copy link

kr2748 commented Nov 15, 2023

keepalive include/exclude props don't work with
When this will be resolved?
We want to use include/exclude props.
I know you are busy and having a hard time, but I hope you can resolve this as soon as possible.

@Clarkkkk
Copy link
Contributor

@Maxbsy @kr2748 It has been fixed and should be available in 3.8.2 soon.

@BoBo-Git
Copy link

@Maxbsy @kr2748 It has been fixed and should be available in 3.8.2 soon.

I can't wait to try out this version, but its performance seems to be different from what I expected. The lifecycle of the component can be triggered normally, but it also triggers data requests on the page, making me feel like the page is being refreshed. However, if I only use keepalive props like <NuxtPage :keepalive={}>, the data requests on the page will not be triggered. I want to know why this is happening.

@BoBo-Git
Copy link

I have made a series of attempts again, and currently this solution can achieve the goal I want.

  1. Configure keepalive as true in nuxt.config.ts.
  2. Remove the keepalive props on the <NuxtPage>.
  3. Control keepalive only through the keepalive property in definePageMeta.

@bigiCrab
Copy link

bigiCrab commented Nov 22, 2023

I try it on NUXT 3.8.2, for page keepalive and it work.

  1. set the name for the page you want to keepalive, not the name in definePageMeta but in the options api.
  2. set <NuxtPage :keepalive="{include: 'KeepAliveComponentName'}" /> in app.vue.

note: you can also control the key by setting the key in definePageMeta

demo: https://stackblitz.com/edit/nuxt-starter-m7k9hv?file=pages%2Falive.vue

@freezyh
Copy link

freezyh commented Nov 22, 2023

I try it on NUXT 3.8.2, for page keepalive and it work.

  1. set the name for the page you want to keepalive, not the name in definePageMeta but in the options api.
  2. set <NuxtPage :keepalive="{include: 'KeepAliveComponentName'}" /> in app.vue.

note: you can also control the key by setting the key in definePageMeta

I don't know how to set the name for the page

my code:
<NuxtPage :keepalive="{ include: 'alive1' }" />

the page src:

G:\study\testnuxt3\pages\alive1\index.vue

It can't work , can you privide a sameple code, thank you very much!

@bigiCrab
Copy link

@freezyh use this to add name

<script>
export default {
  name: 'alive1',
};
</script>

I also update the demo in the above comment.
hope this help!

@freezyh
Copy link

freezyh commented Nov 23, 2023

@freezyh use this to add name

<script>
export default {
  name: 'alive1',
};
</script>

I also update the demo in the above comment. hope this help!

thank you very much, I had wright it by typescript
defineOptions({ name: 'Alive1' })
It works!

@transtone
Copy link

#24024 (comment)

@PNP-cpp
Copy link

PNP-cpp commented Jan 17, 2024

Dynamically changing the include attribute will invalidate the cache. Who knows what happened
html <NuxtPage :page-key="(route) => route.meta.id" :keepalive="{ include: cacheIncludes, max: 10 }" />

@ronannnn
Copy link

Dynamically changing the include attribute will invalidate the cache. Who knows what happened html <NuxtPage :page-key="(route) => route.meta.id" :keepalive="{ include: cacheIncludes, max: 10 }" />

I have the same problem

@tenfei4
Copy link

tenfei4 commented Mar 29, 2024

After testing, in v3.11.1 version, has been able to solve this problem very well, here is an idea, The reason for the problem is keepalive include default read from.matched[0].components.default.__name The value is used as the name of the components, but nuxt defaults to file as the route name.
So we can add the following code to middleware:

 from.matched[0].components.default.__ name = from.name;
useAliveList('add', from.name?. toString());

You can replace the default component name with from.name and add from.name to a list of composables or pinia states.
Then add the following code to app.vue:

<template>
	<NuxtLayout>
		<NuxtLoadingIndicator color="#3366ff" class="shadow-md shadow-blue-400" />
		<NuxtPage :keepalive="{include:alive}" :pageKey="route=>route.fullPath"/>
	</NuxtLayout>
</template>

<script setup lang="ts">
const route=useRoute(),aliveList=useAliveList();
const alive=computed(()=>aliveList.value.join(','));
//watch(()=>alive.value,()=>{ console.log(alive.value); });
</script>

@tenfei4
Copy link

tenfei4 commented Mar 30, 2024

Of course, I hope that the official can match from.matched[0].components.default.__name The default value directly to the value of form.name. That would be even more perfect.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.