diff --git a/packages/nuxt/src/app/components/route-provider.ts b/packages/nuxt/src/app/components/route-provider.ts
index 5111eafec8..6f57fb82f6 100644
--- a/packages/nuxt/src/app/components/route-provider.ts
+++ b/packages/nuxt/src/app/components/route-provider.ts
@@ -4,7 +4,6 @@ import type { RouteLocation, RouteLocationNormalizedLoaded } from '#vue-router'
import { PageRouteSymbol } from './injections'
export const RouteProvider = defineComponent({
- name: 'RouteProvider',
props: {
vnode: {
type: Object as () => VNode,
diff --git a/packages/nuxt/src/pages/runtime/page.ts b/packages/nuxt/src/pages/runtime/page.ts
index 0661be00cf..9c97d02b77 100644
--- a/packages/nuxt/src/pages/runtime/page.ts
+++ b/packages/nuxt/src/pages/runtime/page.ts
@@ -88,20 +88,27 @@ export default defineComponent({
{ onAfterLeave: () => { nuxtApp.callHook('page:transition:finish', routeProps.Component) } }
].filter(Boolean))
+ const keepaliveConfig = props.keepalive ?? routeProps.route.meta.keepalive ?? (defaultKeepaliveConfig as KeepAliveProps)
vnode = _wrapIf(Transition, hasTransition && transitionProps,
- wrapInKeepAlive(props.keepalive ?? routeProps.route.meta.keepalive ?? (defaultKeepaliveConfig as KeepAliveProps), h(Suspense, {
+ wrapInKeepAlive(keepaliveConfig, h(Suspense, {
suspensible: true,
onPending: () => nuxtApp.callHook('page:start', routeProps.Component),
onResolve: () => { nextTick(() => nuxtApp.callHook('page:finish', routeProps.Component).finally(done)) }
}, {
- default: () => h(RouteProvider, {
- key: key || undefined,
- vnode: routeProps.Component,
- route: routeProps.route,
- renderKey: key || undefined,
- trackRootNodes: hasTransition,
- vnodeRef: pageRef
- })
+ default: () => {
+ const providerVNode = h(RouteProvider, {
+ key: key || undefined,
+ vnode: routeProps.Component,
+ route: routeProps.route,
+ renderKey: key || undefined,
+ trackRootNodes: hasTransition,
+ vnodeRef: pageRef
+ })
+ if (import.meta.client && keepaliveConfig) {
+ (providerVNode.type as any).name = (routeProps.Component.type as any).name || (routeProps.Component.type as any).__name || 'RouteProvider'
+ }
+ return providerVNode
+ }
})
)).default()
diff --git a/test/basic.test.ts b/test/basic.test.ts
index d4386c9086..31e3d919e7 100644
--- a/test/basic.test.ts
+++ b/test/basic.test.ts
@@ -2017,6 +2017,87 @@ describe.runIf(isDev())('component testing', () => {
})
})
+describe('keepalive', () => {
+ it('should not keepalive by default', async () => {
+ const { page, consoleLogs } = await renderPage('/keepalive')
+
+ const pageName = 'not-keepalive'
+ await page.click(`#${pageName}`)
+ await page.waitForTimeout(25)
+
+ expect(consoleLogs.map(l => l.text).filter(t => t.includes('keepalive'))).toEqual([`${pageName}: onMounted`])
+
+ await page.close()
+ })
+
+ it('should not keepalive when included in app config but config in nuxt-page is not undefined', async () => {
+ const { page, consoleLogs } = await renderPage('/keepalive')
+
+ const pageName = 'keepalive-in-config'
+ await page.click(`#${pageName}`)
+ await page.waitForTimeout(25)
+
+ expect(consoleLogs.map(l => l.text).filter(t => t.includes('keepalive'))).toEqual([`${pageName}: onMounted`])
+
+ await page.close()
+ })
+
+ it('should not keepalive when included in app config but exclueded in nuxt-page', async () => {
+ const { page, consoleLogs } = await renderPage('/keepalive')
+
+ const pageName = 'not-keepalive-in-nuxtpage'
+ await page.click(`#${pageName}`)
+ await page.waitForTimeout(25)
+
+ expect(consoleLogs.map(l => l.text).filter(t => t.includes('keepalive'))).toEqual([`${pageName}: onMounted`])
+
+ await page.close()
+ })
+
+ it('should keepalive when included in nuxt-page', async () => {
+ const { page, consoleLogs } = await renderPage('/keepalive')
+
+ const pageName = 'keepalive-in-nuxtpage'
+ await page.click(`#${pageName}`)
+ await page.waitForTimeout(25)
+
+ expect(consoleLogs.map(l => l.text).filter(t => t.includes('keepalive'))).toEqual([`${pageName}: onMounted`, `${pageName}: onActivated`])
+
+ await page.close()
+ })
+
+ it('should preserve keepalive config when navigate routes in nuxt-page', async () => {
+ const { page, consoleLogs } = await renderPage('/keepalive')
+
+ await page.click('#keepalive-in-nuxtpage')
+ await page.waitForTimeout(25)
+ await page.click('#keepalive-in-nuxtpage-2')
+ await page.waitForTimeout(25)
+ await page.click('#keepalive-in-nuxtpage')
+ await page.waitForTimeout(25)
+ await page.click('#not-keepalive')
+ await page.waitForTimeout(25)
+ await page.click('#keepalive-in-nuxtpage-2')
+ await page.waitForTimeout(25)
+
+ expect(consoleLogs.map(l => l.text).filter(t => t.includes('keepalive'))).toEqual([
+ 'keepalive-in-nuxtpage: onMounted',
+ 'keepalive-in-nuxtpage: onActivated',
+ 'keepalive-in-nuxtpage: onDeactivated',
+ 'keepalive-in-nuxtpage-2: onMounted',
+ 'keepalive-in-nuxtpage-2: onActivated',
+ 'keepalive-in-nuxtpage: onActivated',
+ 'keepalive-in-nuxtpage-2: onDeactivated',
+ 'keepalive-in-nuxtpage: onDeactivated',
+ 'not-keepalive: onMounted',
+ 'keepalive-in-nuxtpage-2: onActivated',
+ 'not-keepalive: onUnmounted'
+ ])
+
+ await page.close()
+ })
+})
+
function normaliseIslandResult (result: NuxtIslandResponse) {
return {
...result,
diff --git a/test/fixtures/basic/composables/keep-alive.ts b/test/fixtures/basic/composables/keep-alive.ts
new file mode 100644
index 0000000000..bee308628e
--- /dev/null
+++ b/test/fixtures/basic/composables/keep-alive.ts
@@ -0,0 +1,6 @@
+export function useLifecyleLogs (name: string) {
+ onMounted(() => console.log(`${name}: onMounted`))
+ onUnmounted(() => console.log(`${name}: onUnmounted`))
+ onActivated(() => console.log(`${name}: onActivated`))
+ onDeactivated(() => console.log(`${name}: onDeactivated`))
+}
diff --git a/test/fixtures/basic/nuxt.config.ts b/test/fixtures/basic/nuxt.config.ts
index 570b54b62b..5b6d6e469a 100644
--- a/test/fixtures/basic/nuxt.config.ts
+++ b/test/fixtures/basic/nuxt.config.ts
@@ -22,6 +22,9 @@ export default defineNuxtConfig({
{ charset: 'utf-8' },
{ name: 'description', content: 'Nuxt Fixture' }
]
+ },
+ keepalive: {
+ include: ['keepalive-in-config', 'not-keepalive-in-nuxtpage']
}
},
buildDir: process.env.NITRO_BUILD_DIR,
diff --git a/test/fixtures/basic/pages/keepalive.vue b/test/fixtures/basic/pages/keepalive.vue
new file mode 100644
index 0000000000..fdaa673a94
--- /dev/null
+++ b/test/fixtures/basic/pages/keepalive.vue
@@ -0,0 +1,26 @@
+
+
+
+
+
Keepalive Test
+
+ Keepalive Home
+
+
+
+ {{ link }}
+
+
+
+
+
diff --git a/test/fixtures/basic/pages/keepalive/keepalive-in-config.vue b/test/fixtures/basic/pages/keepalive/keepalive-in-config.vue
new file mode 100644
index 0000000000..a057e7427b
--- /dev/null
+++ b/test/fixtures/basic/pages/keepalive/keepalive-in-config.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
Keepalive in Config
+
+
diff --git a/test/fixtures/basic/pages/keepalive/keepalive-in-nuxtpage-2.vue b/test/fixtures/basic/pages/keepalive/keepalive-in-nuxtpage-2.vue
new file mode 100644
index 0000000000..bb4fd440c2
--- /dev/null
+++ b/test/fixtures/basic/pages/keepalive/keepalive-in-nuxtpage-2.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
Keepalive in `nuxt-page` 2
+
+
diff --git a/test/fixtures/basic/pages/keepalive/keepalive-in-nuxtpage.vue b/test/fixtures/basic/pages/keepalive/keepalive-in-nuxtpage.vue
new file mode 100644
index 0000000000..19c4312cca
--- /dev/null
+++ b/test/fixtures/basic/pages/keepalive/keepalive-in-nuxtpage.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
Keepalive in `nuxt-page`
+
+
diff --git a/test/fixtures/basic/pages/keepalive/not-keepalive-in-nuxtpage.vue b/test/fixtures/basic/pages/keepalive/not-keepalive-in-nuxtpage.vue
new file mode 100644
index 0000000000..6d16f7173d
--- /dev/null
+++ b/test/fixtures/basic/pages/keepalive/not-keepalive-in-nuxtpage.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
Not Keepalive in `nuxt-page`
+
+
diff --git a/test/fixtures/basic/pages/keepalive/not-keepalive.vue b/test/fixtures/basic/pages/keepalive/not-keepalive.vue
new file mode 100644
index 0000000000..e7fc57ea43
--- /dev/null
+++ b/test/fixtures/basic/pages/keepalive/not-keepalive.vue
@@ -0,0 +1,9 @@
+
+
+
+
+
Not Keepalive
+
+