diff --git a/src/index.js b/src/index.js index 69c3cc0c8..a4c876f90 100644 --- a/src/index.js +++ b/src/index.js @@ -89,8 +89,22 @@ export default class VueRouter { this.apps.push(app) - // main app already initialized. + // set up app destroyed handler + app.$once('hook:destroyed', () => { + // clean out app from this.apps array once destroyed + const index = this.apps.indexOf(app) + if (index > -1) this.apps.splice(index, 1) + // ensure we still have a main app, unless this is the last app left + if (this.apps.length > 0) this.app = this.apps[0] + }) + + // main app previously initialized if (this.app) { + if (this.app !== this.apps[0]) { + // main app had been destroyed, so replace it with this new app + this.app = this.apps[0] + } + // return as we don't need to set up new history listener return } diff --git a/test/unit/specs/api.spec.js b/test/unit/specs/api.spec.js index 5fce2e885..75abdb2cd 100644 --- a/test/unit/specs/api.spec.js +++ b/test/unit/specs/api.spec.js @@ -1,4 +1,5 @@ import Router from '../../../src/index' +import Vue from 'vue' describe('router.onReady', () => { it('should work', done => { @@ -185,3 +186,89 @@ describe('router.push/replace callbacks', () => { }) }) }) + +describe('router app destroy handling', () => { + Vue.use(Router) + + const router = new Router({ + mode: 'abstract', + routes: [ + { path: '/', component: { name: 'A' }} + ] + }) + + // Add main app + const app1 = new Vue({ + router, + render (h) { return h('div') } + }) + + // Add 2nd app + const app2 = new Vue({ + router, + render (h) { return h('div') } + }) + + // Add 3rd app + const app3 = new Vue({ + router, + render (h) { return h('div') } + }) + + it('router and apps should be defined', () => { + expect(router).toBeDefined() + expect(router instanceof Router).toBe(true) + expect(app1).toBeDefined() + expect(app1 instanceof Vue).toBe(true) + expect(app2).toBeDefined() + expect(app2 instanceof Vue).toBe(true) + expect(app3).toBeDefined() + expect(app3 instanceof Vue).toBe(true) + expect(app1.$router.apps).toBe(app2.$router.apps) + expect(app2.$router.apps).toBe(app3.$router.apps) + expect(app1.$router.app).toBe(app2.$router.app) + expect(app2.$router.app).toBe(app3.$router.app) + }) + + it('should have 3 registered apps', () => { + expect(app1.$router).toBeDefined() + expect(app1.$router.app).toBe(app1) + expect(app1.$router.apps.length).toBe(3) + expect(app1.$router.apps[0]).toBe(app1) + expect(app1.$router.apps[1]).toBe(app2) + expect(app1.$router.apps[2]).toBe(app3) + }) + + it('should remove 2nd destroyed app from this.apps', () => { + app2.$destroy() + expect(app1.$router).toBeDefined() + expect(app1.$router.app).toBeDefined() + expect(app1.$router.app).toBe(app1) + expect(app1.$router.apps.length).toBe(2) + expect(app1.$router.apps[0]).toBe(app1) + expect(app1.$router.apps[1]).toBe(app3) + }) + + it('should remove 1st destroyed app from this.apps and replace this.app', () => { + app1.$destroy() + expect(app3.$router.app).toBe(app3) + expect(app3.$router.apps.length).toBe(1) + expect(app3.$router.apps[0]).toBe(app3) + }) + + it('should remove last destroyed app from this.apps', () => { + app3.$destroy() + expect(app3.$router.app).toBe(app3) + expect(app3.$router.apps.length).toBe(0) + }) + + it('should replace app with new app', () => { + const app4 = new Vue({ + router, + render (h) { return h('div') } + }) + expect(app4.$router.app).toBe(app4) + expect(app4.$router.apps.length).toBe(1) + expect(app4.$router.apps[0]).toBe(app4) + }) +})