diff --git a/src/history/hash.js b/src/history/hash.js index e0ddf2d7c..81ee1fc3c 100644 --- a/src/history/hash.js +++ b/src/history/hash.js @@ -3,6 +3,7 @@ import type Router from '../index' import { History } from './base' import { cleanPath } from '../util/path' +import { isSameRoute, createPlainRoute } from '../util/route' import { getLocation } from './html5' import { setupScroll, handleScroll } from '../util/scroll' import { pushState, replaceState, supportsPushState } from '../util/push-state' @@ -87,8 +88,10 @@ export class HashHistory extends History { } ensureURL (push?: boolean) { - const current = this.current.fullPath - if (getHash() !== current) { + const location = getHash() + const plainRoute = createPlainRoute(location) + if (!isSameRoute(plainRoute, this.current)) { + const current = this.current.fullPath push ? pushHash(current) : replaceHash(current) } } diff --git a/src/history/html5.js b/src/history/html5.js index 9af347fe8..5ae0b1b81 100644 --- a/src/history/html5.js +++ b/src/history/html5.js @@ -3,7 +3,7 @@ import type Router from '../index' import { History } from './base' import { cleanPath } from '../util/path' -import { START } from '../util/route' +import { START, isSameRoute, createPlainRoute } from '../util/route' import { setupScroll, handleScroll } from '../util/scroll' import { pushState, replaceState, supportsPushState } from '../util/push-state' @@ -74,7 +74,9 @@ export class HTML5History extends History { } ensureURL (push?: boolean) { - if (getLocation(this.base) !== this.current.fullPath) { + const location = getLocation(this.base) + const plainRoute = createPlainRoute(location) + if (!isSameRoute(plainRoute, this.current)) { const current = cleanPath(this.base + this.current.fullPath) push ? pushState(current) : replaceState(current) } diff --git a/src/util/route.js b/src/util/route.js index 37a499272..bd8e27bd5 100644 --- a/src/util/route.js +++ b/src/util/route.js @@ -2,6 +2,7 @@ import type VueRouter from '../index' import { stringifyQuery } from './query' +import { normalizeLocation } from './location' const trailingSlashRE = /\/?$/ @@ -96,8 +97,8 @@ export function isSameRoute (a: Route, b: ?Route): boolean { function isObjectEqual (a = {}, b = {}): boolean { // handle null value #1566 if (!a || !b) return a === b - const aKeys = Object.keys(a).sort() - const bKeys = Object.keys(b).sort() + const aKeys = Object.keys(a) + const bKeys = Object.keys(b) if (aKeys.length !== bKeys.length) { return false } @@ -149,3 +150,8 @@ export function handleRouteEntered (route: Route) { } } } +/** just create a route without any added process */ +export function createPlainRoute (url: string): Route { + const location = normalizeLocation(url) + return createRoute(null, location) +} diff --git a/test/unit/specs/route.spec.js b/test/unit/specs/route.spec.js index c9483ab7d..944d32b93 100644 --- a/test/unit/specs/route.spec.js +++ b/test/unit/specs/route.spec.js @@ -1,4 +1,4 @@ -import { isSameRoute, isIncludedRoute } from '../../../src/util/route' +import { isSameRoute, isIncludedRoute, createPlainRoute } from '../../../src/util/route' describe('Route utils', () => { describe('isSameRoute', () => { @@ -13,7 +13,13 @@ describe('Route utils', () => { hash: '#hi', query: { arr: ['1', '2'], foo: 'bar' } } - expect(isSameRoute(a, b)).toBe(true) + const c = { + path: '/a/', // Allow trailing slash + hash: '#hi', + query: { foo: 'bar', arr: [1, 2] } + } + expect(isSameRoute(a, b)).toBe(false) + expect(isSameRoute(a, c)).toBe(true) }) it('name', () => { @@ -28,7 +34,13 @@ describe('Route utils', () => { hash: '#hi', query: { arr: ['1', '2'], foo: 'bar' } } - expect(isSameRoute(a, b)).toBe(true) + const c = { + name: 'a', + hash: '#hi', + query: { foo: 'bar', arr: [1, 2] } + } + expect(isSameRoute(a, b)).toBe(false) + expect(isSameRoute(a, c)).toBe(true) }) it('nested query', () => { @@ -44,7 +56,12 @@ describe('Route utils', () => { path: '/abc', query: { arr: [1, 2], foo: { bar: 'not bar' }} } - expect(isSameRoute(a, b)).toBe(true) + const d = { + path: '/abc', + query: { foo: { bar: 'bar' }, arr: [1, 2] } + } + expect(isSameRoute(a, b)).toBe(false) + expect(isSameRoute(a, d)).toBe(true) expect(isSameRoute(a, c)).toBe(false) }) @@ -150,4 +167,24 @@ describe('Route utils', () => { expect(isIncludedRoute(d, f)).toBe(false) }) }) + + describe('createPlainRoute', () => { + it('path', () => { + const a = { path: '/a?arr=1&foo=bar&arr=2#hi' } + const b = { path: '/a?arr=1&arr=2&foo=bar#hi' } + const c = { path: '/a?foo=bar&arr=1&arr=2#hi' } + const route = { + path: '/a', + hash: '#hi', + query: { foo: 'bar', arr: [1, 2] } + } + const aRoute = createPlainRoute(a) + const bRoute = createPlainRoute(b) + const cRoute = createPlainRoute(c) + expect(isSameRoute(aRoute, route)).toBe(false) + expect(isSameRoute(bRoute, route)).toBe(false) + expect(isSameRoute(bRoute, route)).toBe(false) + expect(isSameRoute(cRoute, route)).toBe(true) + }) + }) })