Detailed Explanation of Frontend Routing Principles and Implementation
Problem Description
Frontend routing is one of the core technologies of modern Single-Page Applications (SPA). It allows switching page content based on URL changes without refreshing the entire page. Interviewers often examine your understanding of frontend routing implementation principles, including the differences between Hash routing and History routing modes, implementation details, and application scenarios.
Breakdown of Key Knowledge Points
1. Why is Frontend Routing Needed?
- Traditional Multi-Page Applications (MPA): Every page jump requires requesting a new page from the server, causing a full refresh and resulting in a less smooth experience.
- Single-Page Applications (SPA): HTML, CSS, and JS are loaded only during the initial load. Subsequent page content switches are handled dynamically via frontend routing, achieving page jumps without refresh, thereby enhancing user experience.
2. Two Modes of Frontend Routing
2.1 Hash Mode
-
Principle: Utilizes the characteristic that changes to the part of the URL after the
#(the hash) do not trigger a page refresh. -
URL Example:
http://example.com/#/home,http://example.com/#/about -
Listening Event: Monitors hash changes via
window.onhashchange. -
Implementation Steps:
- Define a route table: Map hash paths to corresponding components or functions.
- Parse the current hash during initialization and render the corresponding content.
- Listen for hash change events and dynamically update the page.
-
Code Example:
class HashRouter { constructor() { this.routes = {}; // Store route mappings window.addEventListener('hashchange', () => this.handleRouteChange()); } // Register a route addRoute(path, callback) { this.routes[path] = callback; } // Handle route changes handleRouteChange() { const hash = window.location.hash.slice(1) || '/'; // Remove #, default to root path const callback = this.routes[hash]; if (callback) callback(); } // Manual navigation navigate(path) { window.location.hash = path; } } // Usage Example const router = new HashRouter(); router.addRoute('/home', () => { document.getElementById('content').innerHTML = 'Home Page'; }); router.addRoute('/about', () => { document.getElementById('content').innerHTML = 'About Page'; });
2.2 History Mode
-
Principle: Uses the HTML5 History API (
pushState,replaceState) to modify the URL path, and listens for browser forward/back navigation via thepopstateevent. -
URL Example:
http://example.com/home(No#, more aesthetically pleasing). -
Key APIs:
history.pushState(state, title, url): Adds a history entry without triggering a page refresh.history.replaceState(): Replaces the current history entry.window.onpopstate: Listens for browser forward/back operations.
-
Important Notes:
- Requires server support: Since users might directly access subpaths (e.g.,
/home), the server needs to be configured to redirect to the main page; otherwise, a 404 error may be returned. - Only
pushStateandreplaceStatedo not trigger thepopstateevent; the page must be updated manually.
- Requires server support: Since users might directly access subpaths (e.g.,
-
Code Example:
class HistoryRouter { constructor() { this.routes = {}; // Bind click events (intercept default navigation of anchor tags) document.addEventListener('click', (e) => { if (e.target.tagName === 'A') { e.preventDefault(); this.navigate(e.target.getAttribute('href')); } }); window.addEventListener('popstate', () => this.handleRouteChange()); } addRoute(path, callback) { this.routes[path] = callback; } // Manual navigation (using pushState) navigate(path) { history.pushState(null, '', path); this.handleRouteChange(); } handleRouteChange() { const path = window.location.pathname; // Get current path const callback = this.routes[path]; if (callback) callback(); } }
3. Comparison of the Two Modes
| Feature | Hash Mode | History Mode |
|---|---|---|
| URL Aesthetics | Contains #, less aesthetic |
No #, more like a real path |
| Browser Compatibility | Supported by all browsers | Requires IE10+ (History API) |
| Server Configuration | No special configuration needed | Requires redirect configuration to avoid 404 |
| Listening Method | onhashchange |
onpopstate (only listens to forward/back navigation) |
4. Advanced: Dynamic Routing and Parameter Passing
- Dynamic Paths (e.g.,
/user/:id):- Implementation Idea: Convert paths to regular expressions and extract parameters during matching.
- Example: Path
/user/123can be parsed as{ path: '/user/:id', params: { id: '123' } }.
- Query Parameters (e.g.,
?name=foo):- Parse
window.location.searchviaURLSearchParams.
- Parse
5. Optimizations in Practical Applications
- Route Lazy Loading: Combine with dynamic
import()to load components on-demand, improving initial page load speed. - Route Guards: Implement access control through hook functions (e.g.,
beforeEnter).
Summary
The core of frontend routing is monitoring URL changes and mapping them to corresponding views. Hash mode is simple and has good compatibility, while History mode is more aesthetically pleasing but requires server support. Modern frameworks (like Vue Router, React Router) are built upon these two modes, adding advanced features such as dynamic routing and nested routing. Understanding their underlying principles helps in better utilizing and debugging routing-related functionalities.