From b2accc6de744dda1c6cdc02cafa6c5f6002e2eaf Mon Sep 17 00:00:00 2001 From: Nils Date: Sat, 2 Nov 2024 22:28:04 +0100 Subject: [PATCH] feat(nuxt): allow chunk error or manifest update -> reload (#28160) --- .../1.experimental-features.md | 6 +++-- .../plugins/chunk-reload-immediate.client.ts | 23 +++++++++++++++++++ packages/nuxt/src/core/nuxt.ts | 5 ++++ packages/schema/src/config/experimental.ts | 9 +++++--- 4 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 packages/nuxt/src/app/plugins/chunk-reload-immediate.client.ts diff --git a/docs/2.guide/3.going-further/1.experimental-features.md b/docs/2.guide/3.going-further/1.experimental-features.md index dd5d649979..daf4653e53 100644 --- a/docs/2.guide/3.going-further/1.experimental-features.md +++ b/docs/2.guide/3.going-further/1.experimental-features.md @@ -73,14 +73,16 @@ export default defineNuxtConfig({ ## emitRouteChunkError -Emits `app:chunkError` hook when there is an error loading vite/webpack chunks. Default behavior is to perform a hard reload of the new route when a chunk fails to load. +Emits `app:chunkError` hook when there is an error loading vite/webpack chunks. Default behavior is to perform a reload of the new route on navigation to a new route when a chunk fails to load. + +If you set this to `'automatic-immediate'` Nuxt will reload the current route immediatly, instead of waiting for a navigation. This is useful for chunk errors that are not triggered by navigation, e.g., when your Nuxt app fails to load a [lazy component](/docs/guide/directory-structure/components#dynamic-imports). A potential downside of this behavior is undesired reloads, e.g., when your app does not need the chunk that caused the error. You can disable automatic handling by setting this to `false`, or handle chunk errors manually by setting it to `manual`. ```ts twoslash [nuxt.config.ts] export default defineNuxtConfig({ experimental: { - emitRouteChunkError: 'automatic' // or 'manual' or false + emitRouteChunkError: 'automatic' // or 'automatic-immediate', 'manual' or false } }) ``` diff --git a/packages/nuxt/src/app/plugins/chunk-reload-immediate.client.ts b/packages/nuxt/src/app/plugins/chunk-reload-immediate.client.ts new file mode 100644 index 0000000000..ef2c8946ab --- /dev/null +++ b/packages/nuxt/src/app/plugins/chunk-reload-immediate.client.ts @@ -0,0 +1,23 @@ +import { defineNuxtPlugin } from '../nuxt' +import { reloadNuxtApp } from '../composables/chunk' +import { addRouteMiddleware } from '../composables/router' + +const reloadNuxtApp_ = (path: string) => { reloadNuxtApp({ persistState: true, path }) } + +// See https://github.com/nuxt/nuxt/issues/23612 for more context +export default defineNuxtPlugin({ + name: 'nuxt:chunk-reload-immediate', + setup (nuxtApp) { + // Remember `to.path` when navigating to a new path: A `chunkError` may occur during navigation, we then want to then reload at `to.path` + let currentlyNavigationTo: null | string = null + addRouteMiddleware((to) => { + currentlyNavigationTo = to.path + }) + + // Reload when a `chunkError` is thrown + nuxtApp.hook('app:chunkError', () => reloadNuxtApp_(currentlyNavigationTo ?? nuxtApp._route.path)) + + // Reload when the app manifest updates + nuxtApp.hook('app:manifest:update', () => reloadNuxtApp_(nuxtApp._route.path)) + }, +}) diff --git a/packages/nuxt/src/core/nuxt.ts b/packages/nuxt/src/core/nuxt.ts index 6820423944..41b0c29db4 100644 --- a/packages/nuxt/src/core/nuxt.ts +++ b/packages/nuxt/src/core/nuxt.ts @@ -576,6 +576,11 @@ async function initNuxt (nuxt: Nuxt) { if (nuxt.options.experimental.emitRouteChunkError === 'automatic') { addPlugin(resolve(nuxt.options.appDir, 'plugins/chunk-reload.client')) } + // Add experimental immediate page reload support + if (nuxt.options.experimental.emitRouteChunkError === 'automatic-immediate') { + addPlugin(resolve(nuxt.options.appDir, 'plugins/chunk-reload-immediate.client')) + } + // Add experimental session restoration support if (nuxt.options.experimental.restoreState) { addPlugin(resolve(nuxt.options.appDir, 'plugins/restore-state.client')) diff --git a/packages/schema/src/config/experimental.ts b/packages/schema/src/config/experimental.ts index 4f4ef3292e..648beeace3 100644 --- a/packages/schema/src/config/experimental.ts +++ b/packages/schema/src/config/experimental.ts @@ -159,13 +159,16 @@ export default defineUntypedSchema({ * Emit `app:chunkError` hook when there is an error loading vite/webpack * chunks. * - * By default, Nuxt will also perform a hard reload of the new route - * when a chunk fails to load when navigating to a new route. + * By default, Nuxt will also perform a reload of the new route + * when a chunk fails to load when navigating to a new route (`automatic`). + * + * Setting `automatic-immediate` will lead Nuxt to perform a reload of the current route + * right when a chunk fails to load (instead of waiting for navigation). * * You can disable automatic handling by setting this to `false`, or handle * chunk errors manually by setting it to `manual`. * @see [Nuxt PR #19038](https://github.com/nuxt/nuxt/pull/19038) - * @type {false | 'manual' | 'automatic'} + * @type {false | 'manual' | 'automatic' | 'automatic-immediate'} */ emitRouteChunkError: { $resolve: (val) => {