From df022a4a9d429516f7ff6bb88ba112229b8cd145 Mon Sep 17 00:00:00 2001 From: Julien Huang Date: Fri, 27 Oct 2023 16:32:09 +0200 Subject: [PATCH] fix(nuxt): use route key for loading indicator/view transition (#23868) --- .../app/components/nuxt-loading-indicator.ts | 3 +- packages/nuxt/src/app/components/utils.ts | 30 +++++++++++++++++++ .../app/plugins/view-transitions.client.ts | 3 +- 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/packages/nuxt/src/app/components/nuxt-loading-indicator.ts b/packages/nuxt/src/app/components/nuxt-loading-indicator.ts index a3ee0c937a..bf5a11cbd3 100644 --- a/packages/nuxt/src/app/components/nuxt-loading-indicator.ts +++ b/packages/nuxt/src/app/components/nuxt-loading-indicator.ts @@ -1,4 +1,5 @@ import { computed, defineComponent, h, onBeforeUnmount, ref } from 'vue' +import { isChangingPage } from './utils' import { useNuxtApp } from '#app/nuxt' import { useRouter } from '#app/composables/router' @@ -42,7 +43,7 @@ export default defineComponent({ indicator.finish() }) router.beforeResolve((to, from) => { - if (to === from || to.matched.every((comp, index) => comp.components && comp.components?.default === from.matched[index]?.components?.default)) { + if (!isChangingPage(to, from)) { indicator.finish() } }) diff --git a/packages/nuxt/src/app/components/utils.ts b/packages/nuxt/src/app/components/utils.ts index d2bb6b1a21..dbe8d23744 100644 --- a/packages/nuxt/src/app/components/utils.ts +++ b/packages/nuxt/src/app/components/utils.ts @@ -3,6 +3,7 @@ import type { Component, RendererNode } from 'vue' // eslint-disable-next-line import { isString, isPromise, isArray, isObject } from '@vue/shared' import destr from 'destr' +import type { RouteLocationNormalized } from '#vue-router' /** * Internal utility @@ -13,6 +14,35 @@ export const _wrapIf = (component: Component, props: any, slots: any) => { return { default: () => props ? h(component, props, slots) : slots.default?.() } } +// TODO: consider refactoring into single utility +// See https://github.com/nuxt/nuxt/tree/main/packages/nuxt/src/pages/runtime/utils.ts#L8-L19 +function generateRouteKey (route: RouteLocationNormalized) { + const source = route?.meta.key ?? route.path + .replace(/(:\w+)\([^)]+\)/g, '$1') + .replace(/(:\w+)[?+*]/g, '$1') + .replace(/:\w+/g, r => route.params[r.slice(1)]?.toString() || '') + return typeof source === 'function' ? source(route) : source +} + +/** + * Utility used within router guards + * return true if the route has been changed with a page change during navigation + */ +export function isChangingPage (to: RouteLocationNormalized, from: RouteLocationNormalized) { + if (to === from) { return false } + + // If route keys are different then it will result in a rerender + if (generateRouteKey(to) !== generateRouteKey(from)) { return true } + + const areComponentsSame = to.matched.every((comp, index) => + comp.components && comp.components.default === from.matched[index]?.components?.default + ) + if (areComponentsSame) { + return false + } + return true +} + // eslint-disable-next-line no-use-before-define export type SSRBuffer = SSRBufferItem[] & { hasAsync?: boolean } export type SSRBufferItem = string | SSRBuffer | Promise diff --git a/packages/nuxt/src/app/plugins/view-transitions.client.ts b/packages/nuxt/src/app/plugins/view-transitions.client.ts index f0f75fcd2b..cd7ed55fdd 100644 --- a/packages/nuxt/src/app/plugins/view-transitions.client.ts +++ b/packages/nuxt/src/app/plugins/view-transitions.client.ts @@ -1,3 +1,4 @@ +import { isChangingPage } from '#app/components/utils' import { useRouter } from '#app/composables/router' import { defineNuxtPlugin } from '#app/nuxt' @@ -10,7 +11,7 @@ export default defineNuxtPlugin((nuxtApp) => { const router = useRouter() router.beforeResolve((to, from) => { - if (to === from || to.matched.every((comp, index) => comp.components && comp.components?.default === from.matched[index]?.components?.default)) { + if (!isChangingPage(to, from)) { return } const promise = new Promise((resolve, reject) => {