如何使用原生JavaScript实现Angular 2路由?
Got it, let's break down how to implement Angular 2-style routing using plain vanilla JavaScript—no TypeScript, no frameworks, just good old JS. This approach covers the core routing features you'd expect from Angular 2: route configuration, dynamic route parameters, component rendering, and navigation handling.
1. Define Your Route Configuration
First, set up a route config similar to Angular's Routes array. Each route will have a path, a "component" (a function that returns HTML or manipulates the DOM), and optionally a flag for exact matches.
const routes = [ { path: '', component: HomeComponent, exact: true }, { path: 'about', component: AboutComponent }, { path: 'users/:id', component: UserDetailComponent }, { path: '**', component: NotFoundComponent } // Catch-all for 404 ]; // Example "components" - these are just functions that return HTML function HomeComponent() { return `<h1>Home Page</h1><p>Welcome to the vanilla JS routing demo!</p>`; } function AboutComponent() { return `<h1>About Us</h1><p>We're building routing from scratch!</p>`; } function UserDetailComponent(params) { return `<h1>User ${params.id}</h1><p>Viewing details for user ID: ${params.id}</p>`; } function NotFoundComponent() { return `<h1>404 - Page Not Found</h1>`; }
2. Build the Core Router Class
This class will handle URL parsing, route matching, component rendering, and navigation events—just like Angular's Router service.
class Router { constructor(routes, outletId) { this.routes = routes; this.outlet = document.getElementById(outletId); this.init(); } // Initialize router: listen for popstate (back/forward) and initial render init() { window.addEventListener('popstate', () => this.handleRouteChange()); this.handleRouteChange(); } // Get the current path from the hash (hash mode, no server config needed) getCurrentPath() { return window.location.hash.slice(1) || ''; } // Match the current path to a route in the config matchRoute(path) { for (const route of this.routes) { // Convert route path to regex for dynamic parameters (e.g., /users/:id → ^\/users\/(\w+)$) const routeRegex = new RegExp(`^${route.path.replace(/:(\w+)/g, '($1)').replace(/\//g, '\\/')}$`); const match = path.match(routeRegex); if (match) { // Extract dynamic parameters const params = {}; const paramNames = route.path.match(/:(\w+)/g) || []; paramNames.forEach((name, index) => { params[name.slice(1)] = match[index + 1]; }); return { ...route, params }; } // Handle exact matches for root path if (route.exact && path === route.path) { return { ...route, params: {} }; } } // Fallback to catch-all route return this.routes.find(route => route.path === '**'); } // Render the matched component into the outlet renderComponent(route) { if (!this.outlet) return; this.outlet.innerHTML = route.component(route.params); } // Handle route changes (manual navigation or popstate) handleRouteChange() { const path = this.getCurrentPath(); const matchedRoute = this.matchRoute(path); this.renderComponent(matchedRoute); } // Programmatic navigation (like router.navigate()) navigate(path) { window.location.hash = path; } }
3. Add Navigation Links
Create links that work like Angular's routerLink directive. We'll use a custom data-router-link attribute and listen for clicks to trigger navigation.
<!-- Navigation bar --> <nav> <a data-router-link="/">Home</a> <a data-router-link="/about">About</a> <a data-router-link="/users/123">User 123</a> <a data-router-link="/non-existent">404 Test</a> </nav> <!-- Route outlet (where components render) --> <div id="router-outlet"></div>
Add JavaScript to handle these links:
// Initialize the router with our routes and outlet ID const router = new Router(routes, 'router-outlet'); // Handle router link clicks document.addEventListener('click', (e) => { const routerLink = e.target.getAttribute('data-router-link'); if (routerLink) { e.preventDefault(); router.navigate(routerLink); } });
4. Key Features Explained
- Hash Mode: We're using
window.location.hashto avoid needing server-side configuration (for HTML5 mode, you'd need to set up your server to redirect all requests to index.html). - Dynamic Parameters: The router uses regex to parse paths like
/users/:idand extract theidparameter, passing it to the component function. - Exact Matches: The
exactflag ensures the root path ('') only matches when the hash is empty, not every path that starts with/. - Programmatic Navigation: The
navigate()method lets you trigger route changes from code, just like Angular'srouter.navigate().
5. Testing the Implementation
- Save all the code into an HTML file.
- Open it in a browser.
- Click the navigation links to see components render dynamically.
- Use the browser's back/forward buttons—they should work thanks to the
popstatelistener. - Try visiting a non-existent path to see the 404 component.
内容的提问来源于stack exchange,提问作者Nav




