Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions packages/router/__tests__/initialNavigation.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { createApp } from 'vue'
import { JSDOM } from 'jsdom'
import { createRouter, createWebHistory } from '../src'
import { createDom, components, nextNavigation } from './utils'
Expand Down Expand Up @@ -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: `
<router-view />
`,
})
app.use(router)
await router.isReady()
expect(history.location).toBe('/bar')
})
})
27 changes: 26 additions & 1 deletion packages/router/src/history/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isBrowser } from '../utils'
import { removeTrailingSlash } from '../location'
import { removeTrailingSlash, stripBase } from '../location'

export type HistoryLocation = string
/**
Expand Down Expand Up @@ -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
}
27 changes: 1 addition & 26 deletions packages/router/src/history/html5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import {
ValueContainer,
normalizeBase,
createHref,
createCurrentLocation,
HistoryLocation,
} from './common'
import {
computeScrollPosition,
_ScrollPositionNormalized,
} from '../scrollBehavior'
import { warn } from '../warning'
import { stripBase } from '../location'
import { assign } from '../utils'

type PopStateListener = (this: Window, ev: PopStateEvent) => any
Expand All @@ -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<StateEntry>,
Expand Down
15 changes: 11 additions & 4 deletions packages/router/src/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import {
EXPERIMENTAL_Router_Base,
_OnReadyCallback,
} from './experimental/router'
import { createCurrentLocation } from './history/common'

/**
* Options to initialize a {@link Router} instance.
Expand Down Expand Up @@ -730,23 +731,29 @@ 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<HistoryState> | 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,
},
data
)
)
else routerHistory.push(toLocation.fullPath, data)
} else routerHistory.push(toLocation.fullPath, data)
}

// accept current navigation
Expand Down