diff --git a/examples/basic/app.js b/examples/basic/app.js index 2e22b6763..b248d7841 100644 --- a/examples/basic/app.js +++ b/examples/basic/app.js @@ -35,6 +35,7 @@ const Home = { template: '
home
' } const Foo = { template: '
foo
' } const Bar = { template: '
bar
' } const Unicode = { template: '
unicode
' } +const Query = { template: '
query: "{{ $route.params.q }}"
' } // 3. Create the router const router = new VueRouter({ @@ -44,7 +45,8 @@ const router = new VueRouter({ { path: '/', component: Home }, { path: '/foo', component: Foo }, { path: '/bar', component: Bar }, - { path: '/é', component: Unicode } + { path: '/é', component: Unicode }, + { path: '/query/:q', component: Query } ] }) @@ -83,6 +85,7 @@ const vueInstance = new Vue({
  • /foo (replace)
  • +
  • /query/A%
  • / (delay of 500ms)
  • /foo (delay of 500ms)
  • diff --git a/examples/hash-mode/app.js b/examples/hash-mode/app.js index 48b9ab1d6..80a2056ab 100644 --- a/examples/hash-mode/app.js +++ b/examples/hash-mode/app.js @@ -35,6 +35,7 @@ const Home = { template: '
    home
    ' } const Foo = { template: '
    foo
    ' } const Bar = { template: '
    bar
    ' } const Unicode = { template: '
    unicode: {{ $route.params.unicode }}
    ' } +const Query = { template: '
    query: "{{ $route.params.q }}"
    ' } // 3. Create the router const router = new VueRouter({ @@ -45,7 +46,8 @@ const router = new VueRouter({ { path: '/foo', component: Foo }, { path: '/bar', component: Bar }, { path: '/é', component: Unicode }, - { path: '/é/:unicode', component: Unicode } + { path: '/é/:unicode', component: Unicode }, + { path: '/query/:q', component: Query } ] }) @@ -66,6 +68,7 @@ const vueInstance = new Vue({
  • /é/ñ
  • /é/ñ?t=%ñ
  • /é/ñ#é
  • +
  • /query/A%
  • {{ $route.query.t }}
    {{ $route.hash }}
    diff --git a/src/create-matcher.js b/src/create-matcher.js index ab62be430..c67538bc8 100644 --- a/src/create-matcher.js +++ b/src/create-matcher.js @@ -175,7 +175,7 @@ function matchRoute ( path: string, params: Object ): boolean { - const m = path.match(regex) + const m = decodeURI(path).match(regex) if (!m) { return false @@ -185,10 +185,9 @@ function matchRoute ( for (let i = 1, len = m.length; i < len; ++i) { const key = regex.keys[i - 1] - const val = typeof m[i] === 'string' ? decodeURIComponent(m[i]) : m[i] if (key) { // Fix #1994: using * with props: true generates a param named 0 - params[key.name || 'pathMatch'] = val + params[key.name || 'pathMatch'] = m[i] } } diff --git a/src/history/hash.js b/src/history/hash.js index b3372e10d..e0ddf2d7c 100644 --- a/src/history/hash.js +++ b/src/history/hash.js @@ -124,18 +124,6 @@ export function getHash (): string { if (index < 0) return '' href = href.slice(index + 1) - // decode the hash but not the search or hash - // as search(query) is already decoded - // https://github.com/vuejs/vue-router/issues/2708 - const searchIndex = href.indexOf('?') - if (searchIndex < 0) { - const hashIndex = href.indexOf('#') - if (hashIndex > -1) { - href = decodeURI(href.slice(0, hashIndex)) + href.slice(hashIndex) - } else href = decodeURI(href) - } else { - href = decodeURI(href.slice(0, searchIndex)) + href.slice(searchIndex) - } return href } diff --git a/src/history/html5.js b/src/history/html5.js index 8200b3b28..9af347fe8 100644 --- a/src/history/html5.js +++ b/src/history/html5.js @@ -86,7 +86,7 @@ export class HTML5History extends History { } export function getLocation (base: string): string { - let path = decodeURI(window.location.pathname) + let path = window.location.pathname if (base && path.toLowerCase().indexOf(base.toLowerCase()) === 0) { path = path.slice(base.length) } diff --git a/test/e2e/specs/basic.js b/test/e2e/specs/basic.js index 25d3fed3f..1a623c0c6 100644 --- a/test/e2e/specs/basic.js +++ b/test/e2e/specs/basic.js @@ -9,8 +9,8 @@ module.exports = { browser .url('http://localhost:8080/basic/') .waitForElementVisible('#app', 1000) - .assert.count('li', 11) - .assert.count('li a', 11) + .assert.count('li', 12) + .assert.count('li a', 12) // assert correct href with base .assert.attributeContains('li:nth-child(1) a', 'href', '/basic/') .assert.attributeContains('li:nth-child(2) a', 'href', '/basic/foo') @@ -20,6 +20,7 @@ module.exports = { .assert.attributeContains('li:nth-child(6) a', 'href', '/basic/%C3%A9?t=%25%C3%B1') .assert.attributeContains('li:nth-child(7) a', 'href', '/basic/%C3%A9#%25%C3%B1') .assert.attributeContains('li:nth-child(8) a', 'href', '/basic/foo') + .assert.attributeContains('li:nth-child(10) a', 'href', '/basic/query/A%') .assert.containsText('.view', 'home') .click('li:nth-child(2) a') @@ -70,6 +71,15 @@ module.exports = { .assert.cssClassPresent('li:nth-child(8)', 'exact-active') .assert.attributeEquals('li:nth-child(8) a', 'class', '') + // encoded percentage as path param + // https://github.com/vuejs/vue-router/issues/2725 + .url('http://localhost:8080/basic/query/A%25') + .waitForElementVisible('#app', 1000) + .assert.containsText('.view', 'query: "A%"') + .click('li:nth-child(10) a') + .assert.urlEquals('http://localhost:8080/basic/query/A%25') + .assert.containsText('.view', 'query: "A%"') + // Listener cleanup .assert.containsText('#popstate-count', '1 popstate listeners') .click('#unmount') @@ -84,8 +94,8 @@ module.exports = { .waitForElementVisible('#app', 1000) .assert.containsText('.view', 'home') // go to foo with a delay + .click('li:nth-child(12) a') .click('li:nth-child(11) a') - .click('li:nth-child(10) a') .waitFor(300) // we should stay at /basic after the delay .assert.urlEquals('http://localhost:8080/basic/?delay=200') diff --git a/test/e2e/specs/hash-mode.js b/test/e2e/specs/hash-mode.js index 569285a23..15520313f 100644 --- a/test/e2e/specs/hash-mode.js +++ b/test/e2e/specs/hash-mode.js @@ -9,14 +9,15 @@ module.exports = { browser .url('http://localhost:8080/hash-mode/') .waitForElementVisible('#app', 1000) - .assert.count('li', 8) - .assert.count('li a', 7) + .assert.count('li', 9) + .assert.count('li a', 8) .assert.attributeContains('li:nth-child(1) a', 'href', '/hash-mode/#/') .assert.attributeContains('li:nth-child(2) a', 'href', '/hash-mode/#/foo') .assert.attributeContains('li:nth-child(3) a', 'href', '/hash-mode/#/bar') .assert.attributeContains('li:nth-child(5) a', 'href', '/hash-mode/#/%C3%A9') .assert.attributeContains('li:nth-child(6) a', 'href', '/hash-mode/#/%C3%A9/%C3%B1') .assert.attributeContains('li:nth-child(7) a', 'href', '/hash-mode/#/%C3%A9/%C3%B1?t=%25%C3%B1') + .assert.attributeContains('li:nth-child(9) a', 'href', '/hash-mode/#/query/A%') .assert.containsText('.view', 'home') .click('li:nth-child(2) a') @@ -58,6 +59,15 @@ module.exports = { .assert.containsText('.view', 'unicode: ñ') .assert.containsText('#query-t', '%') + // percentage as path param + // https://github.com/vuejs/vue-router/issues/2725 + .url('http://localhost:8080/hash-mode/#/query/A%25') + .waitForElementVisible('#app', 1000) + .assert.containsText('.view', 'query: "A%"') + .click('li:nth-child(9) a') + .assert.urlEquals('http://localhost:8080/hash-mode/#/query/A%25') + .assert.containsText('.view', 'query: "A%"') + // Listener cleanup .assert.containsText('#popstate-count', '1 popstate listeners') .click('#unmount')