From 992fd4bf226e209ba6791b5abf91734f4ede1d9c Mon Sep 17 00:00:00 2001
From: ichaoX <11764590+ichaoX@users.noreply.github.com>
Date: Wed, 11 Jan 2023 13:22:27 +0800
Subject: [PATCH] fix(history): Resolve the href in `` correctly (#3819)
* Resolve the href in `` correctly.
* In hash mode, the hash in base is automatically removed and the base trailing slash is distinguished.
* By default the result of `router.resolve().href` is the same as the actual switched URL.
---
examples/hash-mode/app.js | 2 +-
src/history/base.js | 22 +++++++++++++++-------
src/history/hash.js | 12 ++++++++++++
src/history/html5.js | 6 +++---
src/router.js | 4 +++-
src/util/path.js | 15 +++++++++++++--
6 files changed, 47 insertions(+), 14 deletions(-)
diff --git a/examples/hash-mode/app.js b/examples/hash-mode/app.js
index 16475ffc8..ba9f3f459 100644
--- a/examples/hash-mode/app.js
+++ b/examples/hash-mode/app.js
@@ -40,7 +40,7 @@ const Query = { template: '
query: "{{ $route.params.q }}"
' }
// 3. Create the router
const router = new VueRouter({
mode: 'hash',
- base: __dirname,
+ base: require('path').join(__dirname, '/'),
routes: [
{ path: '/', component: Home }, // all paths are defined without the hash.
{ path: '/foo', component: Foo },
diff --git a/src/history/base.js b/src/history/base.js
index 4751cd97a..68d6a8898 100644
--- a/src/history/base.js
+++ b/src/history/base.js
@@ -272,19 +272,27 @@ function normalizeBase (base: ?string): string {
if (inBrowser) {
// respect tag
const baseEl = document.querySelector('base')
- base = (baseEl && baseEl.getAttribute('href')) || '/'
- // strip full URL origin
- base = base.replace(/^https?:\/\/[^\/]+/, '')
+ base = (baseEl && baseEl.getAttribute('href') && typeof baseEl.href === 'string') ? baseEl.href : ''
+ if (base) {
+ const href = window.location.href
+ const locationOrigin = href.replace(/^([^\/]+:\/\/[^\/]*)?.*$/, '$1')
+ const baseOrigin = base.replace(/^([^\/]+:\/\/[^\/]*)?.*$/, '$1')
+ if (locationOrigin === baseOrigin) {
+ base = base.slice(baseOrigin.length)
+ } else {
+ // XXX: hash and history modes do not support cross-origin
+ base = locationOrigin
+ }
+ }
} else {
- base = '/'
+ base = ''
}
}
// make sure there's the starting slash
- if (base.charAt(0) !== '/') {
+ if (base && base.charAt(0) !== '/' && !base.match(/^[^\/]+:\/\//)) {
base = '/' + base
}
- // remove trailing slash
- return base.replace(/\/$/, '')
+ return base
}
function resolveQueue (
diff --git a/src/history/hash.js b/src/history/hash.js
index e0ddf2d7c..a4b4b71d8 100644
--- a/src/history/hash.js
+++ b/src/history/hash.js
@@ -10,6 +10,7 @@ import { pushState, replaceState, supportsPushState } from '../util/push-state'
export class HashHistory extends History {
constructor (router: Router, base: ?string, fallback: boolean) {
super(router, base)
+ this.base = normalizeHashBase(this.base, !base)
// check history fallback deeplinking
if (fallback && checkFallback(this.base)) {
return
@@ -135,6 +136,17 @@ function getUrl (path) {
return `${base}#${path}`
}
+function normalizeHashBase (base: string, replace: ?boolean): string {
+ if (!base) return ''
+ if (replace) {
+ const hasOrigin = !!base.match(/^[^\/]+:\/\/[^\/]+/)
+ base = window.location.href
+ // XXX: keep origin for possible cross-origin cases
+ if (!hasOrigin) base = base.replace(/^[^\/]+:\/\/[^\/]+/, '')
+ }
+ return base.replace(/#.*$/, '')
+}
+
function pushHash (path) {
if (supportsPushState) {
pushState(getUrl(path))
diff --git a/src/history/html5.js b/src/history/html5.js
index faaa02fcd..b20185009 100644
--- a/src/history/html5.js
+++ b/src/history/html5.js
@@ -58,7 +58,7 @@ export class HTML5History extends History {
push (location: RawLocation, onComplete?: Function, onAbort?: Function) {
const { current: fromRoute } = this
this.transitionTo(location, route => {
- pushState(cleanPath(this.base + route.fullPath))
+ pushState(cleanPath(route.fullPath, this.base))
handleScroll(this.router, route, fromRoute, false)
onComplete && onComplete(route)
}, onAbort)
@@ -67,7 +67,7 @@ export class HTML5History extends History {
replace (location: RawLocation, onComplete?: Function, onAbort?: Function) {
const { current: fromRoute } = this
this.transitionTo(location, route => {
- replaceState(cleanPath(this.base + route.fullPath))
+ replaceState(cleanPath(route.fullPath, this.base))
handleScroll(this.router, route, fromRoute, false)
onComplete && onComplete(route)
}, onAbort)
@@ -75,7 +75,7 @@ export class HTML5History extends History {
ensureURL (push?: boolean) {
if (getLocation(this.base) !== this.current.fullPath) {
- const current = cleanPath(this.base + this.current.fullPath)
+ const current = cleanPath(this.current.fullPath, this.base)
push ? pushState(current) : replaceState(current)
}
}
diff --git a/src/router.js b/src/router.js
index 5f677dc82..3a9cbb224 100644
--- a/src/router.js
+++ b/src/router.js
@@ -279,7 +279,9 @@ function registerHook (list: Array, fn: Function): Function {
function createHref (base: string, fullPath: string, mode) {
var path = mode === 'hash' ? '#' + fullPath : fullPath
- return base ? cleanPath(base + '/' + path) : path
+ // '/main.html#/foo' should not be converted to '/main.html/#/foo'
+ if (base && mode !== 'hash') base = base.replace(/\/?$/, '/')
+ return base ? cleanPath(path, base) : path
}
// We cannot remove this as it would be a breaking change
diff --git a/src/util/path.js b/src/util/path.js
index 9d048437f..efebc8967 100644
--- a/src/util/path.js
+++ b/src/util/path.js
@@ -69,6 +69,17 @@ export function parsePath (path: string): {
}
}
-export function cleanPath (path: string): string {
- return path.replace(/\/(?:\s*\/)+/g, '/')
+export function cleanPath (path: string, base: ?string): string {
+ let prefix = ''
+ if (base) {
+ // allow base to specify an origin
+ const match = base.match(/^((?:[^\/]+:)?\/\/)/)
+ if (match) {
+ prefix = match[0]
+ path = base.slice(prefix.length) + path
+ } else {
+ path = base + path
+ }
+ }
+ return prefix + path.replace(/\/(?:\s*\/)+/g, '/')
}