feat(router): add proper server side redirection to navigateTo (#3684)

Co-authored-by: pooya parsa <pyapar@gmail.com>
This commit is contained in:
François Risoud 2022-03-16 22:39:47 +01:00 committed by GitHub
parent 93da7978fb
commit 99705f77c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 67 additions and 9 deletions

View File

@ -4,6 +4,7 @@ import type { MetaInfo } from 'vue-meta'
import type VueRouter from 'vue-router' import type VueRouter from 'vue-router'
import type { Location, Route } from 'vue-router' import type { Location, Route } from 'vue-router'
import type { RuntimeConfig } from '@nuxt/schema' import type { RuntimeConfig } from '@nuxt/schema'
import { sendRedirect } from 'h3'
import defu from 'defu' import defu from 'defu'
import { useNuxtApp } from './app' import { useNuxtApp } from './app'
@ -172,12 +173,23 @@ const isProcessingMiddleware = () => {
return false return false
} }
export const navigateTo = (to: Route) => { export interface NavigateToOptions {
replace?: boolean
}
export const navigateTo = (to: Route, options: NavigateToOptions = {}) => {
if (isProcessingMiddleware()) { if (isProcessingMiddleware()) {
return to return to
} }
const router: VueRouter = process.server ? useRouter() : (window as any).$nuxt.$router const router = useRouter()
return router.push(to) 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. */ /** This will abort navigation within a Nuxt route middleware handler. */

View File

@ -1,4 +1,5 @@
import type { Router, RouteLocationNormalizedLoaded, NavigationGuard, RouteLocationNormalized, RouteLocationRaw } from 'vue-router' import type { Router, RouteLocationNormalizedLoaded, NavigationGuard, RouteLocationNormalized, RouteLocationRaw } from 'vue-router'
import { sendRedirect } from 'h3'
import { useNuxtApp } from '#app' import { useNuxtApp } from '#app'
export const useRouter = () => { export const useRouter = () => {
@ -45,12 +46,23 @@ const isProcessingMiddleware = () => {
return false return false
} }
export const navigateTo = (to: RouteLocationRaw) => { export interface NavigateToOptions {
replace?: boolean
}
export const navigateTo = (to: RouteLocationRaw, options: NavigateToOptions = {}) => {
if (isProcessingMiddleware()) { if (isProcessingMiddleware()) {
return to return to
} }
const router: Router = process.server ? useRouter() : (window as any).$nuxt.$router const router = useRouter()
return router.push(to) 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. */ /** This will abort navigation within a Nuxt route middleware handler. */

View File

@ -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', () => { describe('middlewares', () => {
it('should redirect to index with global middleware', async () => { it('should redirect to index with global middleware', async () => {
const html = await $fetch('/redirect/') const html = await $fetch('/redirect/')

View File

@ -8,7 +8,16 @@ describe('fixtures:bridge', async () => {
server: true server: true
}) })
it('Render hello world', async () => { describe('pages', () => {
expect(await $fetch('/')).to.contain('Hello Vue 2!') 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!')
})
}) })
}) })

View File

@ -0,0 +1,7 @@
<template>
<div>You should not see me</div>
</template>
<script setup>
navigateTo('/', { replace: true })
</script>

View File

@ -51,7 +51,7 @@ describe('middleware', () => {
addRouteMiddleware('example', (to, from) => { addRouteMiddleware('example', (to, from) => {
expectTypeOf(to).toMatchTypeOf<RouteLocationNormalizedLoaded>() expectTypeOf(to).toMatchTypeOf<RouteLocationNormalizedLoaded>()
expectTypeOf(from).toMatchTypeOf<RouteLocationNormalizedLoaded>() expectTypeOf(from).toMatchTypeOf<RouteLocationNormalizedLoaded>()
expectTypeOf(navigateTo).toMatchTypeOf<(to: RouteLocationRaw) => RouteLocationRaw | Promise<void | NavigationFailure>>() expectTypeOf(navigateTo).toMatchTypeOf<(to: RouteLocationRaw) => RouteLocationRaw | Promise<void | unknown | NavigationFailure>>()
navigateTo('/') navigateTo('/')
abortNavigation() abortNavigation()
abortNavigation('error string') abortNavigation('error string')

View File

@ -0,0 +1,7 @@
<template>
<div>You should not see me</div>
</template>
<script setup>
navigateTo('/', { replace: true })
</script>