Skip to content

Conversation

@Azurewarth0920
Copy link

@Azurewarth0920 Azurewarth0920 commented Nov 8, 2025

fix: #2584

In the first navigation, we regard the current browser location as the target, as the state of routerHistory maybe stale when initializing the router.

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of browser history state during initial navigation to properly respect replaced states.
  • Tests

    • Added test case verifying correct behavior when browser history is replaced at initial navigation.

@netlify
Copy link

netlify bot commented Nov 8, 2025

Deploy Preview for vue-router canceled.

Name Link
🔨 Latest commit 2055833
🔍 Latest deploy log https://app.netlify.com/projects/vue-router/deploys/690f683b3b414f0007835fee

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 8, 2025

Walkthrough

A test case and refactoring address an initial navigation bug where replaced browser history states are ignored. A new createCurrentLocation function is extracted to normalize location objects and is used during first navigation to capture the actual current browser state instead of a stale cached location.

Changes

Cohort / File(s) Summary
Test Case
packages/router/__tests__/initialNavigation.spec.ts
Added test verifying that replaced history state is respected immediately after initial navigation.
Location Creation Abstraction
packages/router/src/history/common.ts
Introduced new exported function createCurrentLocation(base, location) that normalizes a HistoryLocation from a window.Location-like object, handling hash-based bases and path derivation.
HTML5 History Refactor
packages/router/src/history/html5.ts
Replaced local createCurrentLocation implementation with import from common.ts; removed local stripBase import as it's no longer needed.
Router Navigation Fix
packages/router/src/router.ts
Modified finalizeNavigation to use createCurrentLocation(routerHistory.base, location) for initial push navigations with replace flag, ensuring the current browser location is captured instead of a potentially stale one.

Sequence Diagram

sequenceDiagram
    participant App as Vue App
    participant Router as Router
    participant History as Browser History
    
    rect rgb(200, 220, 255)
    Note over App,History: Before: Initial Navigation uses stale location
    App->>Router: createRouter()
    Router->>History: Read location (caches it)
    App->>History: replaceState("/bar")
    App->>Router: use(router)
    Router->>Router: finalizeNavigation
    Router->>History: push(cachedLocation) ✗ Wrong!
    end
    
    rect rgb(200, 255, 220)
    Note over App,History: After: Initial Navigation reads current location
    App->>Router: createRouter()
    App->>History: replaceState("/bar")
    App->>Router: use(router)
    Router->>Router: finalizeNavigation
    Router->>History: createCurrentLocation(base, window.location)
    Router->>History: replace(currentLocation) ✓ Correct!
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • The refactoring introduces a new shared function; verify the createCurrentLocation logic correctly handles both hash-based and standard bases
  • Examine the conditional logic in finalizeNavigation to ensure createCurrentLocation is called only for initial navigations and doesn't affect subsequent navigations
  • Validate that the test case accurately reproduces the original issue and confirms the fix

Poem

🐰 A rabbit hops through history's door,
No more bouncing back and forth!
The location now remembered true,
When replacing what browsers knew. 🌿

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main fix: addressing router history bouncing back during initial navigation.
Linked Issues check ✅ Passed The PR implementation correctly addresses issue #2584 by using current browser location on first navigation instead of stale routerHistory.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the router history bounce-back issue; no unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/router/src/router.ts (1)

739-756: Core fix correctly addresses the bouncing-back issue.

The implementation properly resolves issue #2584 by using the current browser location (createCurrentLocation(routerHistory.base, location)) during first navigation instead of a potentially stale cached location. This ensures that any history.replaceState calls made between router creation and mounting are respected.

Key improvements:

  • The condition replace || isFirstNavigation correctly handles both explicit replace operations and initial navigation
  • The isBrowser check safely guards against SSR environments before accessing the global location object
  • Existing behavior for subsequent navigations is preserved

Consider renaming toLocationPath to toLocationHref or similar, since it contains the full location (path + search + hash) rather than just the path:

-        const toLocationPath =
+        const toLocationHref =
           isFirstNavigation && isBrowser
             ? createCurrentLocation(routerHistory.base, location)
             : toLocation.fullPath

         routerHistory.replace(
-          toLocationPath,
+          toLocationHref,
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a74df76 and 2055833.

📒 Files selected for processing (4)
  • packages/router/__tests__/initialNavigation.spec.ts (2 hunks)
  • packages/router/src/history/common.ts (2 hunks)
  • packages/router/src/history/html5.ts (1 hunks)
  • packages/router/src/router.ts (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-11T15:22:32.526Z
Learnt from: posva
Repo: vuejs/router PR: 2415
File: packages/router/src/experimental/index.ts:43-44
Timestamp: 2025-08-11T15:22:32.526Z
Learning: In the Vue Router codebase, files within the src directory should not import from src/index to avoid circular dependencies. Deep imports like `../matcher/types` are intentional and necessary for maintaining proper dependency hierarchy.

Applied to files:

  • packages/router/__tests__/initialNavigation.spec.ts
🧬 Code graph analysis (3)
packages/router/__tests__/initialNavigation.spec.ts (1)
packages/router/__tests__/utils.ts (1)
  • newRouter (171-178)
packages/router/src/router.ts (2)
packages/router/src/utils/env.ts (1)
  • isBrowser (1-1)
packages/router/src/history/common.ts (1)
  • createCurrentLocation (192-210)
packages/router/src/history/common.ts (1)
packages/router/src/location.ts (1)
  • stripBase (153-158)
🔇 Additional comments (6)
packages/router/__tests__/initialNavigation.spec.ts (1)

79-90: LGTM! Test effectively validates the fix.

The test case correctly verifies that when window.history.replaceState is called after router creation but before the app mounts, the router respects the replaced state rather than bouncing back to a stale location. This directly addresses issue #2584.

packages/router/src/history/html5.ts (2)

10-10: Good refactoring to use shared implementation.

Importing createCurrentLocation from the common module eliminates code duplication and centralizes location normalization logic.


50-50: Consistent use of shared location normalization.

Both call sites correctly use the shared createCurrentLocation function for normalizing browser locations.

Also applies to: 163-163

packages/router/src/history/common.ts (2)

2-2: Appropriate import for the new function.

The stripBase import is correctly added to support the createCurrentLocation implementation.


187-210: Well-implemented location normalization function.

The createCurrentLocation function correctly handles both hash-based and normal routing modes:

  • For hash bases, it extracts the path from location.hash and ensures proper formatting
  • For normal bases, it strips the base from the pathname and preserves search/hash parameters

This centralized implementation will help maintain consistency across history implementations.

packages/router/src/router.ts (1)

75-75: Appropriate import for initial navigation fix.

The createCurrentLocation import is correctly added to support capturing the current browser location during first navigation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Edge case: the location bounce back after installing router instance to instance of Vue

1 participant