跪拜 Guibai
← All articles
Frontend · React.js

Hash vs. History Routing: What Actually Happens in the Browser

By 许我半盏清茶 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Choosing between hash and history routing determines whether you need server-side fallback configuration and whether your URLs will be crawlable. Getting this wrong means 404s on refresh or ugly URLs that break shareability and analytics.

Summary

A single-page application switches views by intercepting navigation and swapping DOM content, but without routing, the URL never changes. Front-end routing solves this by maintaining a mapping table between paths and components, updating the address bar, and rendering the correct view — all without a full page load.

Hash routing relies on the fragment identifier after `#`. Changes to the hash don't trigger a navigation request, and the `hashchange` event makes it trivial to wire up. The implementation is dead simple and works everywhere, but the `#` in the URL looks clunky and hurts SEO.

History routing uses the `pushState` and `replaceState` methods to change the URL path cleanly, with `popstate` handling back/forward button presses. The result is a normal-looking URL, but it introduces a server-side dependency: a direct visit or refresh on any path will 404 unless the server is configured to fall back to the entry HTML file.

Takeaways
Hash routing uses the fragment after `#`; changes to it never trigger a page refresh.
`location.hash` reads the current hash, and the `hashchange` event fires whenever it changes.
History routing uses `history.pushState` to change the URL path without a reload.
The `popstate` event fires on back/forward navigation, not on `pushState` or `replaceState` calls.
History mode produces clean URLs but requires server-side fallback to avoid 404s on direct access.
Hash mode works without any server configuration and has broad legacy browser support.
Both approaches share the same core loop: intercept navigation, update the URL, match a route, and render a component.
Conclusions

Hash routing is often dismissed as legacy, but its zero-config deployment makes it the safer default for static sites and embedded widgets where you can't control the server.

The 404 problem in history mode is not a browser bug — it's a consequence of the server treating every path as a real resource request, which SPAs fundamentally break.

Most frameworks abstract these two modes behind a single API, but understanding the underlying events explains why `popstate` fires on back/forward but not on `pushState` — a distinction that trips up custom router implementations.

The mapping-table pattern — an array of path/component pairs — is the same regardless of mode, which means the routing strategy is a pluggable concern, not a structural one.

Concepts & terms
Single-Page Application (SPA)
A web app that loads a single HTML document and dynamically updates the view as the user interacts, avoiding full page reloads.
hashchange event
A browser event that fires when the fragment identifier (the part after `#`) of the current URL changes.
history.pushState
A History API method that adds a new entry to the browser's session history stack and updates the URL without triggering a page load.
popstate event
A window event dispatched when the active history entry changes, such as when the user clicks the back or forward button.
Route mapping table
A data structure (often an array of objects) that associates URL path patterns with the components or functions that should render for those paths.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗