Skip to content

Commit a775fb1

Browse files
authored
fix(hash): only decode path (#2711)
Fix #2708
1 parent 8009b9c commit a775fb1

File tree

5 files changed

+51
-8
lines changed

5 files changed

+51
-8
lines changed

examples/basic/app.js

+4
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,11 @@ new Vue({
4040
<a>/bar</a>
4141
</router-link>
4242
<li><router-link to="/é">/é</router-link></li>
43+
<li><router-link to="/é?t=%25ñ">/é?t=%ñ</router-link></li>
44+
<li><router-link to="/é#%25ñ">/é#%25ñ</router-link></li>
4345
</ul>
46+
<pre id="query-t">{{ $route.query.t }}</pre>
47+
<pre id="hash">{{ $route.hash }}</pre>
4448
<router-view class="view"></router-view>
4549
</div>
4650
`

examples/hash-mode/app.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ Vue.use(VueRouter)
1010
const Home = { template: '<div>home</div>' }
1111
const Foo = { template: '<div>foo</div>' }
1212
const Bar = { template: '<div>bar</div>' }
13-
const Unicode = { template: '<div>unicode</div>' }
13+
const Unicode = { template: '<div>unicode: {{ $route.params.unicode }}</div>' }
1414

1515
// 3. Create the router
1616
const router = new VueRouter({
@@ -20,7 +20,8 @@ const router = new VueRouter({
2020
{ path: '/', component: Home }, // all paths are defined without the hash.
2121
{ path: '/foo', component: Foo },
2222
{ path: '/bar', component: Bar },
23-
{ path: '/é', component: Unicode }
23+
{ path: '/é', component: Unicode },
24+
{ path: '/é/:unicode', component: Unicode }
2425
]
2526
})
2627

@@ -38,7 +39,12 @@ new Vue({
3839
<li><router-link to="/bar">/bar</router-link></li>
3940
<router-link tag="li" to="/bar">/bar</router-link>
4041
<li><router-link to="/é">/é</router-link></li>
42+
<li><router-link to="/é/ñ">/é/ñ</router-link></li>
43+
<li><router-link to="/é/ñ?t=%25ñ">/é/ñ?t=%ñ</router-link></li>
44+
<li><router-link to="/é/ñ#é">/é/ñ#é</router-link></li>
4145
</ul>
46+
<pre id="query-t">{{ $route.query.t }}</pre>
47+
<pre id="hash">{{ $route.hash }}</pre>
4248
<router-view class="view"></router-view>
4349
</div>
4450
`

src/history/hash.js

+18-2
Original file line numberDiff line numberDiff line change
@@ -100,9 +100,25 @@ function ensureSlash (): boolean {
100100
export function getHash (): string {
101101
// We can't use window.location.hash here because it's not
102102
// consistent across browsers - Firefox will pre-decode it!
103-
const href = window.location.href
103+
let href = window.location.href
104104
const index = href.indexOf('#')
105-
return index === -1 ? '' : decodeURI(href.slice(index + 1))
105+
// empty path
106+
if (index < 0) return ''
107+
108+
href = href.slice(index + 1)
109+
// decode the hash but not the search or hash
110+
// as search(query) is already decoded
111+
// https://github.com/vuejs/vue-router/issues/2708
112+
const searchIndex = href.indexOf('?')
113+
if (searchIndex < 0) {
114+
const hashIndex = href.indexOf('#')
115+
if (hashIndex > -1) href = decodeURI(href.slice(0, hashIndex)) + href.slice(hashIndex)
116+
else href = decodeURI(href)
117+
} else {
118+
if (searchIndex > -1) href = decodeURI(href.slice(0, searchIndex)) + href.slice(searchIndex)
119+
}
120+
121+
return href
106122
}
107123

108124
function getUrl (path) {

test/e2e/specs/basic.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ module.exports = {
33
browser
44
.url('http://localhost:8080/basic/')
55
.waitForElementVisible('#app', 1000)
6-
.assert.count('li', 5)
7-
.assert.count('li a', 5)
6+
.assert.count('li', 7)
7+
.assert.count('li a', 7)
88
// assert correct href with base
99
.assert.attributeContains('li:nth-child(1) a', 'href', '/basic/')
1010
.assert.attributeContains('li:nth-child(2) a', 'href', '/basic/foo')

test/e2e/specs/hash-mode.js

+19-2
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ module.exports = {
33
browser
44
.url('http://localhost:8080/hash-mode/')
55
.waitForElementVisible('#app', 1000)
6-
.assert.count('li', 5)
7-
.assert.count('li a', 4)
6+
.assert.count('li', 8)
7+
.assert.count('li a', 7)
88
.assert.attributeContains('li:nth-child(1) a', 'href', '/hash-mode/#/')
99
.assert.attributeContains('li:nth-child(2) a', 'href', '/hash-mode/#/foo')
1010
.assert.attributeContains('li:nth-child(3) a', 'href', '/hash-mode/#/bar')
1111
.assert.attributeContains('li:nth-child(5) a', 'href', '/hash-mode/#/%C3%A9')
12+
.assert.attributeContains('li:nth-child(6) a', 'href', '/hash-mode/#/%C3%A9/%C3%B1')
13+
.assert.attributeContains('li:nth-child(7) a', 'href', '/hash-mode/#/%C3%A9/%C3%B1?t=%25%C3%B1')
1214
.assert.containsText('.view', 'home')
1315

1416
.click('li:nth-child(2) a')
@@ -31,9 +33,24 @@ module.exports = {
3133
.url('http://localhost:8080/hash-mode/#/foo')
3234
.waitForElementVisible('#app', 1000)
3335
.assert.containsText('.view', 'foo')
36+
// direct visit encoded unicode
3437
.url('http://localhost:8080/hash-mode/#/%C3%A9')
3538
.waitForElementVisible('#app', 1000)
3639
.assert.containsText('.view', 'unicode')
40+
// direct visit raw unicode
41+
.url('http://localhost:8080/hash-mode/#/%C3%A9/%C3%B1')
42+
.waitForElementVisible('#app', 1000)
43+
.assert.containsText('.view', 'unicode: ñ')
44+
// TODO: Doesn't seem to work on PhantomJS
45+
// .click('li:nth-child(7)')
46+
// .assert.urlEquals('http://localhost:8080/hash-mode/#/%C3%A9/%C3%B1?t=%25')
47+
// .assert.containsText('.view', 'unicode: ñ')
48+
// .assert.containsText('#query-t', '%')
49+
// direct visit
50+
.url('http://localhost:8080/hash-mode/#/%C3%A9/%C3%B1?t=%25')
51+
.waitForElementVisible('#app', 1000)
52+
.assert.containsText('.view', 'unicode: ñ')
53+
.assert.containsText('#query-t', '%')
3754
.end()
3855
}
3956
}

0 commit comments

Comments
 (0)