Skip to content

Commit 8910979

Browse files
Domino9697posva
authored andcommitted
fix(abstract history): allow router.back in abstract mode when 2 consecutive same routes appear in history stack (#2771)
* fix(AbstractHistory): Fix router.back in abstract mode Fix abstract mode's router.back() when 2 consecutive same routes appear in the history stack array fix #2607 * refactor(history): use an Error object when navigatin to the same place
1 parent 64785a9 commit 8910979

File tree

4 files changed

+75
-25
lines changed

4 files changed

+75
-25
lines changed

src/history/abstract.js

+15-6
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
import type Router from '../index'
44
import { History } from './base'
5+
import { NavigationDuplicated } from './errors'
56

67
export class AbstractHistory extends History {
7-
index: number;
8-
stack: Array<Route>;
8+
index: number
9+
stack: Array<Route>
910

1011
constructor (router: Router, base: ?string) {
1112
super(router, base)
@@ -34,10 +35,18 @@ export class AbstractHistory extends History {
3435
return
3536
}
3637
const route = this.stack[targetIndex]
37-
this.confirmTransition(route, () => {
38-
this.index = targetIndex
39-
this.updateRoute(route)
40-
})
38+
this.confirmTransition(
39+
route,
40+
() => {
41+
this.index = targetIndex
42+
this.updateRoute(route)
43+
},
44+
err => {
45+
if (err instanceof NavigationDuplicated) {
46+
this.index = targetIndex
47+
}
48+
}
49+
)
4150
}
4251

4352
getCurrentLocation () {

src/history/base.js

+21-16
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,25 @@ import {
1111
flatMapComponents,
1212
resolveAsyncComponents
1313
} from '../util/resolve-components'
14+
import { NavigationDuplicated } from './errors'
1415

1516
export class History {
16-
router: Router;
17-
base: string;
18-
current: Route;
19-
pending: ?Route;
20-
cb: (r: Route) => void;
21-
ready: boolean;
22-
readyCbs: Array<Function>;
23-
readyErrorCbs: Array<Function>;
24-
errorCbs: Array<Function>;
17+
router: Router
18+
base: string
19+
current: Route
20+
pending: ?Route
21+
cb: (r: Route) => void
22+
ready: boolean
23+
readyCbs: Array<Function>
24+
readyErrorCbs: Array<Function>
25+
errorCbs: Array<Function>
2526

2627
// implemented by sub-classes
27-
+go: (n: number) => void;
28-
+push: (loc: RawLocation) => void;
29-
+replace: (loc: RawLocation) => void;
30-
+ensureURL: (push?: boolean) => void;
31-
+getCurrentLocation: () => string;
28+
+go: (n: number) => void
29+
+push: (loc: RawLocation) => void
30+
+replace: (loc: RawLocation) => void
31+
+ensureURL: (push?: boolean) => void
32+
+getCurrentLocation: () => string
3233

3334
constructor (router: Router, base: ?string) {
3435
this.router = router
@@ -87,7 +88,11 @@ export class History {
8788
confirmTransition (route: Route, onComplete: Function, onAbort?: Function) {
8889
const current = this.current
8990
const abort = err => {
90-
if (isError(err)) {
91+
// after merging https://github.com/vuejs/vue-router/pull/2771 we
92+
// When the user navigates through history through back/forward buttons
93+
// we do not want to throw the error. We only throw it if directly calling
94+
// push/replace. That's why it's not included in isError
95+
if (!(err instanceof NavigationDuplicated) && isError(err)) {
9196
if (this.errorCbs.length) {
9297
this.errorCbs.forEach(cb => { cb(err) })
9398
} else {
@@ -103,7 +108,7 @@ export class History {
103108
route.matched.length === current.matched.length
104109
) {
105110
this.ensureURL()
106-
return abort()
111+
return abort(new NavigationDuplicated(route))
107112
}
108113

109114
const {

src/history/errors.js

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export class NavigationDuplicated extends Error {
2+
constructor () {
3+
super('Navigating to current location is not allowed')
4+
Object.setPrototypeOf(this, new.target.prototype)
5+
}
6+
}

test/unit/specs/node.spec.js

+33-3
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,42 @@ describe('Usage in Node', () => {
2727
const router = new VueRouter({
2828
routes: [
2929
{ path: '/', component: Foo },
30-
{ path: '/bar', component: Bar, children: [
31-
{ path: 'baz', component: Baz }
32-
] }
30+
{
31+
path: '/bar',
32+
component: Bar,
33+
children: [{ path: 'baz', component: Baz }]
34+
}
3335
]
3436
})
3537
expect(router.getMatchedComponents('/')).toEqual([Foo])
3638
expect(router.getMatchedComponents('/bar/baz')).toEqual([Bar, Baz])
3739
})
40+
41+
it('should navigate through history with same consecutive routes in history stack', () => {
42+
const success = jasmine.createSpy('complete')
43+
const error = jasmine.createSpy('error')
44+
const router = new VueRouter({
45+
routes: [
46+
{ path: '/', component: { name: 'foo' }},
47+
{ path: '/bar', component: { name: 'bar' }}
48+
]
49+
})
50+
router.push('/', success, error)
51+
expect(success).toHaveBeenCalledTimes(1)
52+
expect(error).toHaveBeenCalledTimes(0)
53+
router.push('/bar', success, error)
54+
expect(success).toHaveBeenCalledTimes(2)
55+
expect(error).toHaveBeenCalledTimes(0)
56+
router.push('/', success, error)
57+
expect(success).toHaveBeenCalledTimes(3)
58+
expect(error).toHaveBeenCalledTimes(0)
59+
router.replace('/bar', success, error)
60+
expect(success).toHaveBeenCalledTimes(4)
61+
expect(error).toHaveBeenCalledTimes(0)
62+
spyOn(console, 'warn')
63+
router.back()
64+
expect(router.history.current.path).toBe('/bar')
65+
router.back()
66+
expect(router.history.current.path).toBe('/')
67+
})
3868
})

0 commit comments

Comments
 (0)