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 = () => { const mount = () => {
_app.$mount('#<%= globals.id %>') _app.$mount('#<%= globals.id %>')
// Add afterEach router hooks
router.afterEach(normalizeComponents)
router.afterEach(fixPrepatch.bind(_app))
// Listen for first Vue update // Listen for first Vue update
Vue.nextTick(() => { Vue.nextTick(() => {
// Call window.{{globals.readyCallback}} callbacks // Call window.{{globals.readyCallback}} callbacks
@ -671,11 +675,9 @@ async function mountApp(__app) {
_app.$loading = {} // To avoid error while _app.$nuxt does not exist _app.$loading = {} // To avoid error while _app.$nuxt does not exist
if (NUXT.error) _app.error(NUXT.error) if (NUXT.error) _app.error(NUXT.error)
// Add router hooks // Add beforeEach router hooks
router.beforeEach(loadAsyncComponents.bind(_app)) router.beforeEach(loadAsyncComponents.bind(_app))
router.beforeEach(render.bind(_app)) router.beforeEach(render.bind(_app))
router.afterEach(normalizeComponents)
router.afterEach(fixPrepatch.bind(_app))
// If page already is server rendered // If page already is server rendered
if (NUXT.serverRendered) { if (NUXT.serverRendered) {
@ -684,20 +686,30 @@ async function mountApp(__app) {
} }
// First render on client-side // First render on client-side
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) => { render.call(_app, router.currentRoute, router.currentRoute, (path) => {
// If not redirected // If not redirected
if (!path) { if (!path) {
normalizeComponents(router.currentRoute, router.currentRoute) clientFirstMount()
showNextPage.call(_app, router.currentRoute)
// Don't call fixPrepatch.call(_app, router.currentRoute, router.currentRoute) since it's first render
mount()
return return
} }
// Push the path and then mount app // Add a one-time afterEach hook to
router.push(path, () => mount(), (err) => { // mount the app wait for redirect and route gets resolved
if (!err) return mount() const unregisterHook = router.afterEach((to, from) => {
errorHandler(err) 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' chunk: '[name].js'
} }
}, },
router: {
middleware: 'middleware'
},
plugins: [ plugins: [
'~/plugins/error.js' '~/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 const { $data } = window.$nuxt.$route.matched[0].instances.default
expect(Object.keys($data).length).toBe(1) 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 // Close server and ask nuxt to stop listening to file changes