fix(vue-app): prevent mounting page twice on redirect (#5361)

This commit is contained in:
noe132 2019-03-29 22:06:35 +08:00 committed by Pooya Parsa
parent 7ce30935fa
commit 2d73e8aeba
5 changed files with 94 additions and 11 deletions

View File

@ -649,6 +649,10 @@ async function mountApp(__app) {
const mount = () => {
_app.$mount('#<%= globals.id %>')
// Add afterEach router hooks
router.afterEach(normalizeComponents)
router.afterEach(fixPrepatch.bind(_app))
// Listen for first Vue update
Vue.nextTick(() => {
// Call window.{{globals.readyCallback}} callbacks
@ -671,11 +675,9 @@ async function mountApp(__app) {
_app.$loading = {} // To avoid error while _app.$nuxt does not exist
if (NUXT.error) _app.error(NUXT.error)
// Add router hooks
// Add beforeEach router hooks
router.beforeEach(loadAsyncComponents.bind(_app))
router.beforeEach(render.bind(_app))
router.afterEach(normalizeComponents)
router.afterEach(fixPrepatch.bind(_app))
// If page already is server rendered
if (NUXT.serverRendered) {
@ -684,20 +686,30 @@ async function mountApp(__app) {
}
// First render on client-side
render.call(_app, router.currentRoute, router.currentRoute, (path) => {
// If not redirected
if (!path) {
const clientFirstMount = () => {
normalizeComponents(router.currentRoute, router.currentRoute)
showNextPage.call(_app, router.currentRoute)
// Don't call fixPrepatch.call(_app, router.currentRoute, router.currentRoute) since it's first render
mount()
}
render.call(_app, router.currentRoute, router.currentRoute, (path) => {
// If not redirected
if (!path) {
clientFirstMount()
return
}
// Push the path and then mount app
router.push(path, () => mount(), (err) => {
if (!err) return mount()
errorHandler(err)
// Add a one-time afterEach hook to
// mount the app wait for redirect and route gets resolved
const unregisterHook = router.afterEach((to, from) => {
unregisterHook()
clientFirstMount()
})
// Push the path and let route to be resolved
router.push(path, undefined, (err) => {
if (err) errorHandler(err)
})
})
}

View File

@ -0,0 +1,14 @@
export default function ({ route, redirect }) {
const redirectPathRegexp = /^\/redirect(\d+)$/
const match = route.path.match(redirectPathRegexp)
if (match) {
const number = parseInt(match[1])
if (number === 1) {
redirect('/redirect-done')
} else {
redirect(`/redirect${number - 1}`)
}
}
}

View File

@ -15,6 +15,9 @@ export default {
chunk: '[name].js'
}
},
router: {
middleware: 'middleware'
},
plugins: [
'~/plugins/error.js'
]

View File

@ -0,0 +1,17 @@
<template>
<div>Redirect Done Page</div>
</template>
<script>
export default {
layout: 'default',
created() {
// eslint-disable-next-line no-console
console.log('redirect-done created')
},
mounted() {
// eslint-disable-next-line no-console
console.log('redirect-done mounted')
}
}
</script>

View File

@ -99,6 +99,43 @@ describe('spa', () => {
const { $data } = window.$nuxt.$route.matched[0].instances.default
expect(Object.keys($data).length).toBe(1)
consola.log.mockClear()
})
test('/redirect-done (no redirect)', async () => {
const { html } = await renderRoute('/redirect-done')
expect(html).toContain('<div>Redirect Done Page</div>')
expect(consola.log).toHaveBeenCalledWith('redirect-done created')
expect(consola.log).toHaveBeenCalledWith('redirect-done mounted')
expect(consola.log).toHaveBeenCalledTimes(2)
consola.log.mockClear()
})
test('/redirect1 (redirect 1 time)', async () => {
const { html } = await renderRoute('/redirect1')
expect(html).toContain('<div>Redirect Done Page</div>')
expect(consola.log).toHaveBeenCalledWith('redirect-done created')
expect(consola.log).toHaveBeenCalledWith('redirect-done mounted')
expect(consola.log).toHaveBeenCalledTimes(2)
consola.log.mockClear()
})
test('/redirect2 (redirect 2 times)', async () => {
const { html } = await renderRoute('/redirect2')
expect(html).toContain('<div>Redirect Done Page</div>')
expect(consola.log).toHaveBeenCalledWith('redirect-done created')
expect(consola.log).toHaveBeenCalledWith('redirect-done mounted')
expect(consola.log).toHaveBeenCalledTimes(2)
consola.log.mockClear()
})
test('/redirect10 (redirect 10 times)', async () => {
const { html } = await renderRoute('/redirect10')
expect(html).toContain('<div>Redirect Done Page</div>')
expect(consola.log).toHaveBeenCalledWith('redirect-done created')
expect(consola.log).toHaveBeenCalledWith('redirect-done mounted')
expect(consola.log).toHaveBeenCalledTimes(2)
consola.log.mockClear()
})
// Close server and ask nuxt to stop listening to file changes