diff --git a/packages/bridge/src/runtime/composables.ts b/packages/bridge/src/runtime/composables.ts index dfb2ebc23e..f6afe057e0 100644 --- a/packages/bridge/src/runtime/composables.ts +++ b/packages/bridge/src/runtime/composables.ts @@ -4,6 +4,7 @@ import type { MetaInfo } from 'vue-meta' import type VueRouter from 'vue-router' import type { Location, Route } from 'vue-router' import type { RuntimeConfig } from '@nuxt/schema' +import { sendRedirect } from 'h3' import defu from 'defu' import { useNuxtApp } from './app' @@ -172,12 +173,23 @@ const isProcessingMiddleware = () => { return false } -export const navigateTo = (to: Route) => { +export interface NavigateToOptions { + replace?: boolean +} + +export const navigateTo = (to: Route, options: NavigateToOptions = {}) => { if (isProcessingMiddleware()) { return to } - const router: VueRouter = process.server ? useRouter() : (window as any).$nuxt.$router - return router.push(to) + const router = useRouter() + if (process.server && useNuxtApp().ssrContext) { + // Server-side redirection using h3 res from ssrContext + const res = useNuxtApp().ssrContext?.res + const redirectLocation = router.resolve(to).route.fullPath + return sendRedirect(res, redirectLocation) + } + // Client-side redirection using vue-router + return options.replace ? router.replace(to) : router.push(to) } /** This will abort navigation within a Nuxt route middleware handler. */ diff --git a/packages/nuxt3/src/app/composables/router.ts b/packages/nuxt3/src/app/composables/router.ts index f5daf0a345..c6c133b867 100644 --- a/packages/nuxt3/src/app/composables/router.ts +++ b/packages/nuxt3/src/app/composables/router.ts @@ -1,4 +1,5 @@ import type { Router, RouteLocationNormalizedLoaded, NavigationGuard, RouteLocationNormalized, RouteLocationRaw } from 'vue-router' +import { sendRedirect } from 'h3' import { useNuxtApp } from '#app' export const useRouter = () => { @@ -45,12 +46,23 @@ const isProcessingMiddleware = () => { return false } -export const navigateTo = (to: RouteLocationRaw) => { +export interface NavigateToOptions { + replace?: boolean +} + +export const navigateTo = (to: RouteLocationRaw, options: NavigateToOptions = {}) => { if (isProcessingMiddleware()) { return to } - const router: Router = process.server ? useRouter() : (window as any).$nuxt.$router - return router.push(to) + const router = useRouter() + if (process.server && useNuxtApp().ssrContext) { + // Server-side redirection using h3 res from ssrContext + const res = useNuxtApp().ssrContext?.res + const redirectLocation = router.resolve(to).fullPath + return sendRedirect(res, redirectLocation) + } + // Client-side redirection using vue-router + return options.replace ? router.replace(to) : router.push(to) } /** This will abort navigation within a Nuxt route middleware handler. */ diff --git a/test/basic.test.ts b/test/basic.test.ts index a7ad8688c9..9bd324f3b0 100644 --- a/test/basic.test.ts +++ b/test/basic.test.ts @@ -94,6 +94,17 @@ describe('fixtures:basic', async () => { }) }) + describe('navigate', () => { + it('should redirect to index with navigateTo', async () => { + const html = await $fetch('/navigate-to/') + + // Snapshot + // expect(html).toMatchInlineSnapshot() + + expect(html).toContain('Hello Nuxt 3!') + }) + }) + describe('middlewares', () => { it('should redirect to index with global middleware', async () => { const html = await $fetch('/redirect/') diff --git a/test/bridge.test.ts b/test/bridge.test.ts index 4e375854dd..018920cd99 100644 --- a/test/bridge.test.ts +++ b/test/bridge.test.ts @@ -8,7 +8,16 @@ describe('fixtures:bridge', async () => { server: true }) - it('Render hello world', async () => { - expect(await $fetch('/')).to.contain('Hello Vue 2!') + describe('pages', () => { + it('render hello world', async () => { + expect(await $fetch('/')).to.contain('Hello Vue 2!') + }) + }) + + describe('navigate', () => { + it('should redirect to index with navigateTo', async () => { + const html = await $fetch('/navigate-to/') + expect(html).toContain('Hello Vue 2!') + }) }) }) diff --git a/test/fixtures/basic/pages/navigate-to.vue b/test/fixtures/basic/pages/navigate-to.vue new file mode 100644 index 0000000000..717569e2df --- /dev/null +++ b/test/fixtures/basic/pages/navigate-to.vue @@ -0,0 +1,7 @@ + + + diff --git a/test/fixtures/basic/types.ts b/test/fixtures/basic/types.ts index 20561e1231..fef78eedf5 100644 --- a/test/fixtures/basic/types.ts +++ b/test/fixtures/basic/types.ts @@ -51,7 +51,7 @@ describe('middleware', () => { addRouteMiddleware('example', (to, from) => { expectTypeOf(to).toMatchTypeOf() expectTypeOf(from).toMatchTypeOf() - expectTypeOf(navigateTo).toMatchTypeOf<(to: RouteLocationRaw) => RouteLocationRaw | Promise>() + expectTypeOf(navigateTo).toMatchTypeOf<(to: RouteLocationRaw) => RouteLocationRaw | Promise>() navigateTo('/') abortNavigation() abortNavigation('error string') diff --git a/test/fixtures/bridge/pages/navigate-to.vue b/test/fixtures/bridge/pages/navigate-to.vue new file mode 100644 index 0000000000..717569e2df --- /dev/null +++ b/test/fixtures/bridge/pages/navigate-to.vue @@ -0,0 +1,7 @@ + + +