diff --git a/packages/nuxt/src/app/composables/router.ts b/packages/nuxt/src/app/composables/router.ts index 6e86a087ce..7b4bcb6abf 100644 --- a/packages/nuxt/src/app/composables/router.ts +++ b/packages/nuxt/src/app/composables/router.ts @@ -1,4 +1,4 @@ -import { getCurrentInstance, hasInjectionContext, inject, onUnmounted } from 'vue' +import { getCurrentInstance, hasInjectionContext, inject, onScopeDispose } from 'vue' import type { Ref } from 'vue' import type { NavigationFailure, NavigationGuard, RouteLocationNormalized, RouteLocationPathRaw, RouteLocationRaw, Router, useRoute as _useRoute, useRouter as _useRouter } from '#vue-router' import { sanitizeStatusCode } from 'h3' @@ -30,12 +30,12 @@ export const onBeforeRouteLeave = (guard: NavigationGuard) => { if (to === from) { return } return guard(to, from, next) }) - onUnmounted(unsubscribe) + onScopeDispose(unsubscribe) } export const onBeforeRouteUpdate = (guard: NavigationGuard) => { const unsubscribe = useRouter().beforeEach(guard) - onUnmounted(unsubscribe) + onScopeDispose(unsubscribe) } export interface RouteMiddleware { @@ -182,6 +182,8 @@ export const navigateTo = (to: RouteLocationRaw | undefined | null, options?: Na // Client-side redirection using vue-router if (isExternal) { + // Run any cleanup steps for the current scope, like ending BroadcastChannel + nuxtApp._scope.stop() if (options?.replace) { location.replace(toPath) } else { diff --git a/packages/nuxt/src/app/nuxt.ts b/packages/nuxt/src/app/nuxt.ts index 2f2554d609..053eed35a3 100644 --- a/packages/nuxt/src/app/nuxt.ts +++ b/packages/nuxt/src/app/nuxt.ts @@ -1,6 +1,6 @@ /* eslint-disable no-use-before-define */ -import { getCurrentInstance, hasInjectionContext, reactive } from 'vue' -import type { App, Ref, VNode, onErrorCaptured } from 'vue' +import { effectScope, getCurrentInstance, hasInjectionContext, reactive } from 'vue' +import type { App, EffectScope, Ref, VNode, onErrorCaptured } from 'vue' import type { RouteLocationNormalizedLoaded } from '#vue-router' import type { HookCallback, Hookable } from 'hookable' import { createHooks } from 'hookable' @@ -98,6 +98,8 @@ interface _NuxtApp { [key: string]: unknown + /** @internal */ + _scope: EffectScope /** @internal */ _asyncDataPromises: Record | undefined> /** @internal */ @@ -202,6 +204,7 @@ export interface CreateOptions { export function createNuxtApp (options: CreateOptions) { let hydratingCount = 0 const nuxtApp: NuxtApp = { + _scope: effectScope(), provide: undefined, globalName: 'nuxt', versions: { @@ -217,7 +220,7 @@ export function createNuxtApp (options: CreateOptions) { static: { data: {} }, - runWithContext: (fn: any) => callWithNuxt(nuxtApp, fn), + runWithContext: (fn: any) => nuxtApp._scope.run(() => callWithNuxt(nuxtApp, fn)), isHydrating: import.meta.client, deferHydration () { if (!nuxtApp.isHydrating) { return () => {} }