diff --git a/packages/router/__tests__/initialNavigation.spec.ts b/packages/router/__tests__/initialNavigation.spec.ts
index 4e2e63787..280c9d4f4 100644
--- a/packages/router/__tests__/initialNavigation.spec.ts
+++ b/packages/router/__tests__/initialNavigation.spec.ts
@@ -1,3 +1,4 @@
+import { createApp } from 'vue'
import { JSDOM } from 'jsdom'
import { createRouter, createWebHistory } from '../src'
import { createDom, components, nextNavigation } from './utils'
@@ -74,4 +75,17 @@ describe('Initial Navigation', () => {
await nextNavigation(router)
expect(router.currentRoute.value).toMatchObject({ path: '/' })
})
+
+ it('Should respect the replaced state right after the initial navigation', async () => {
+ const { router, history } = newRouter('/')
+ window.history.replaceState({}, '', '/bar')
+ const app = createApp({
+ template: `
+
+ `,
+ })
+ app.use(router)
+ await router.isReady()
+ expect(history.location).toBe('/bar')
+ })
})
diff --git a/packages/router/src/history/common.ts b/packages/router/src/history/common.ts
index 0abf48150..0c5bf4793 100644
--- a/packages/router/src/history/common.ts
+++ b/packages/router/src/history/common.ts
@@ -1,5 +1,5 @@
import { isBrowser } from '../utils'
-import { removeTrailingSlash } from '../location'
+import { removeTrailingSlash, stripBase } from '../location'
export type HistoryLocation = string
/**
@@ -183,3 +183,28 @@ const BEFORE_HASH_RE = /^[^#]+#/
export function createHref(base: string, location: HistoryLocation): string {
return base.replace(BEFORE_HASH_RE, '#') + location
}
+
+/**
+ * Creates a normalized history location from a window.location object
+ * @param base - The base path
+ * @param location - The window.location object
+ */
+export function createCurrentLocation(
+ base: string,
+ location: Location
+): HistoryLocation {
+ const { pathname, search, hash } = location
+ // allows hash bases like #, /#, #/, #!, #!/, /#!/, or even /folder#end
+ const hashPos = base.indexOf('#')
+ if (hashPos > -1) {
+ let slicePos = hash.includes(base.slice(hashPos))
+ ? base.slice(hashPos).length
+ : 1
+ let pathFromHash = hash.slice(slicePos)
+ // prepend the starting slash to hash so the url starts with /#
+ if (pathFromHash[0] !== '/') pathFromHash = '/' + pathFromHash
+ return stripBase(pathFromHash, '')
+ }
+ const path = stripBase(pathname, base)
+ return path + search + hash
+}
diff --git a/packages/router/src/history/html5.ts b/packages/router/src/history/html5.ts
index 79c5cedf0..c89cb54f2 100644
--- a/packages/router/src/history/html5.ts
+++ b/packages/router/src/history/html5.ts
@@ -7,6 +7,7 @@ import {
ValueContainer,
normalizeBase,
createHref,
+ createCurrentLocation,
HistoryLocation,
} from './common'
import {
@@ -14,7 +15,6 @@ import {
_ScrollPositionNormalized,
} from '../scrollBehavior'
import { warn } from '../warning'
-import { stripBase } from '../location'
import { assign } from '../utils'
type PopStateListener = (this: Window, ev: PopStateEvent) => any
@@ -30,31 +30,6 @@ interface StateEntry extends HistoryState {
scroll: _ScrollPositionNormalized | null | false
}
-/**
- * Creates a normalized history location from a window.location object
- * @param base - The base path
- * @param location - The window.location object
- */
-function createCurrentLocation(
- base: string,
- location: Location
-): HistoryLocation {
- const { pathname, search, hash } = location
- // allows hash bases like #, /#, #/, #!, #!/, /#!/, or even /folder#end
- const hashPos = base.indexOf('#')
- if (hashPos > -1) {
- let slicePos = hash.includes(base.slice(hashPos))
- ? base.slice(hashPos).length
- : 1
- let pathFromHash = hash.slice(slicePos)
- // prepend the starting slash to hash so the url starts with /#
- if (pathFromHash[0] !== '/') pathFromHash = '/' + pathFromHash
- return stripBase(pathFromHash, '')
- }
- const path = stripBase(pathname, base)
- return path + search + hash
-}
-
function useHistoryListeners(
base: string,
historyState: ValueContainer,
diff --git a/packages/router/src/router.ts b/packages/router/src/router.ts
index 533a1b1fa..28f14f5f7 100644
--- a/packages/router/src/router.ts
+++ b/packages/router/src/router.ts
@@ -72,6 +72,7 @@ import {
EXPERIMENTAL_Router_Base,
_OnReadyCallback,
} from './experimental/router'
+import { createCurrentLocation } from './history/common'
/**
* Options to initialize a {@link Router} instance.
@@ -730,15 +731,21 @@ export function createRouter(options: RouterOptions): Router {
// only consider as push if it's not the first navigation
const isFirstNavigation = from === START_LOCATION_NORMALIZED
const state: Partial | null = !isBrowser ? {} : history.state
-
// change URL only if the user did a push/replace and if it's not the initial navigation because
// it's just reflecting the url
if (isPush) {
// on the initial navigation, we want to reuse the scroll position from
// history state if it exists
- if (replace || isFirstNavigation)
+ if (replace || isFirstNavigation) {
+ // In the first navigation, we regard the current browser location as the target,
+ // As the state of routerHistory maybe stale when initializing the router.
+ const toLocationPath =
+ isFirstNavigation && isBrowser
+ ? createCurrentLocation(routerHistory.base, location)
+ : toLocation.fullPath
+
routerHistory.replace(
- toLocation.fullPath,
+ toLocationPath,
assign(
{
scroll: isFirstNavigation && state && state.scroll,
@@ -746,7 +753,7 @@ export function createRouter(options: RouterOptions): Router {
data
)
)
- else routerHistory.push(toLocation.fullPath, data)
+ } else routerHistory.push(toLocation.fullPath, data)
}
// accept current navigation