路由系统
Angular Router 是 Angular 官方的路由库,用于管理单页应用中的导航。本章将详细介绍路由的配置和使用。
路由基础
配置路由
首先在应用配置中启用路由:
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
export const appConfig: ApplicationConfig = {
providers: [
provideRouter(routes)
]
};
定义路由配置:
import { Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { UserListComponent } from './user-list.component';
import { UserDetailComponent } from './user-detail.component';
export const routes: Routes = [
{ path: '', component: HomeComponent },
{ path: 'users', component: UserListComponent },
{ path: 'users/:id', component: UserDetailComponent },
{ path: '**', redirectTo: '' }
];
路由出口
在组件模板中使用 router-outlet 显示路由组件:
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
template: `
<nav>
<a routerLink="/">首页</a>
<a routerLink="/users">用户列表</a>
</nav>
<router-outlet></router-outlet>
`,
imports: [RouterOutlet]
})
export class AppComponent {}
路由链接
使用 routerLink 指令创建导航链接:
import { Component } from '@angular/core';
import { RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-nav',
template: `
<nav>
<!-- 基本链接 -->
<a routerLink="/">首页</a>
<!-- 带参数的链接 -->
<a [routerLink]="['/users', userId]">用户详情</a>
<!-- 带查询参数 -->
<a [routerLink]="['/users']" [queryParams]="{ page: 1, size: 10 }">
用户列表
</a>
<!-- 激活状态样式 -->
<a routerLink="/home" routerLinkActive="active" [routerLinkActiveOptions]="{ exact: true }">
首页
</a>
</nav>
`,
imports: [RouterLink, RouterLinkActive]
})
export class NavComponent {
userId = 123;
}
获取路由参数
路径参数
使用 :param 语法定义路径参数:
// 路由配置
{ path: 'users/:id', component: UserDetailComponent }
在组件中获取参数:
import { Component, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-user-detail',
template: `
<h2>用户详情</h2>
<p>用户 ID: {{ userId() }}</p>
`
})
export class UserDetailComponent {
private route = inject(ActivatedRoute);
// 使用 Signal 获取参数(推荐)
userId = this.route.snapshot.paramMap.get('id');
// 或者使用 Observable
ngOnInit() {
this.route.paramMap.subscribe(params => {
const id = params.get('id');
console.log('用户 ID:', id);
});
}
}
查询参数
import { Component, inject } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'app-search',
template: `
<h2>搜索结果</h2>
<p>关键词: {{ keyword() }}</p>
<p>页码: {{ page() }}</p>
`
})
export class SearchComponent {
private route = inject(ActivatedRoute);
// 使用 Signal 获取查询参数
keyword = this.route.snapshot.queryParamMap.get('q');
page = this.route.snapshot.queryParamMap.get('page') || '1';
}
导航时传递查询参数:
import { Component, inject } from '@angular/core';
import { Router } from '@angular/router';
@Component({
selector: 'app-search-form',
template: `
<input #searchInput>
<button (click)="search(searchInput.value)">搜索</button>
`
})
export class SearchFormComponent {
private router = inject(Router);
search(keyword: string) {
this.router.navigate(['/search'], {
queryParams: { q: keyword, page: 1 }
});
}
}
子路由
子路由允许在父路由组件中嵌套显示子组件:
// 路由配置
export const routes: Routes = [
{
path: 'admin',
component: AdminComponent,
children: [
{ path: '', redirectTo: 'dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'users', component: AdminUsersComponent },
{ path: 'settings', component: SettingsComponent }
]
}
];
父组件模板:
import { Component } from '@angular/core';
import { RouterOutlet, RouterLink, RouterLinkActive } from '@angular/router';
@Component({
selector: 'app-admin',
template: `
<div class="admin-layout">
<aside class="sidebar">
<a routerLink="dashboard" routerLinkActive="active">仪表盘</a>
<a routerLink="users" routerLinkActive="active">用户管理</a>
<a routerLink="settings" routerLinkActive="active">系统设置</a>
</aside>
<main class="content">
<router-outlet></router-outlet>
</main>
</div>
`,
imports: [RouterOutlet, RouterLink, RouterLinkActive]
})
export class AdminComponent {}
路由守卫
路由守卫用于控制路由的访问权限。
CanActivate 守卫
控制是否可以进入路由:
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from './auth.service';
export const authGuard: CanActivateFn = (route, state) => {
const auth = inject(AuthService);
const router = inject(Router);
if (auth.isLoggedIn()) {
return true;
}
// 重定向到登录页
return router.createUrlTree(['/login'], {
queryParams: { returnUrl: state.url }
});
};
在路由配置中使用:
export const routes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivate: [authGuard]
}
];
CanActivateChild 守卫
控制是否可以进入子路由:
export const adminGuard: CanActivateChildFn = (route, state) => {
const auth = inject(AuthService);
if (auth.hasRole('admin')) {
return true;
}
return inject(Router).createUrlTree(['/forbidden']);
};
// 路由配置
{
path: 'admin',
component: AdminComponent,
canActivateChild: [adminGuard],
children: [
{ path: 'users', component: AdminUsersComponent }
]
}
CanDeactivate 守卫
控制是否可以离开路由(如未保存表单提醒):
import { CanDeactivateFn } from '@angular/router';
export interface CanComponentDeactivate {
canDeactivate: () => boolean;
}
export const canDeactivateGuard: CanDeactivateFn<CanComponentDeactivate> = (component) => {
return component.canDeactivate ? component.canDeactivate() : true;
};
在组件中实现接口:
import { Component } from '@angular/core';
@Component({
selector: 'app-edit-form',
template: `
<form>
<input [(ngModel)]="data" name="data">
<button (click)="save()">保存</button>
</form>
`
})
export class EditFormComponent implements CanComponentDeactivate {
data = '';
saved = false;
canDeactivate(): boolean {
if (this.saved) return true;
return confirm('您有未保存的更改,确定要离开吗?');
}
save() {
// 保存逻辑
this.saved = true;
}
}
CanMatch 守卫
控制是否匹配某个路由:
import { CanMatchFn } from '@angular/router';
export const featureFlagGuard: CanMatchFn = (route, segments) => {
const config = inject(ConfigService);
return config.isFeatureEnabled('new-feature');
};
// 路由配置
{
path: 'feature',
canMatch: [featureFlagGuard],
component: NewFeatureComponent
}
懒加载
懒加载可以按需加载模块,减少初始加载时间:
// 使用 loadComponent 加载单个组件
export const routes: Routes = [
{
path: 'admin',
loadComponent: () => import('./admin/admin.component')
.then(m => m.AdminComponent)
}
];
// 使用 loadChildren 加载子路由
export const routes: Routes = [
{
path: 'admin',
loadChildren: () => import('./admin/admin.routes')
.then(m => m.ADMIN_ROUTES)
}
];
子路由文件 admin.routes.ts:
import { Routes } from '@angular/router';
export const ADMIN_ROUTES: Routes = [
{
path: '',
component: AdminComponent,
children: [
{ path: 'dashboard', component: DashboardComponent },
{ path: 'users', component: AdminUsersComponent }
]
}
];
编程式导航
import { Component, inject } from '@angular/core';
import { Router, NavigationExtras } from '@angular/router';
@Component({
selector: 'app-navigation',
template: `
<button (click)="goHome()">返回首页</button>
<button (click)="goToUser()">用户详情</button>
<button (click)="goBack()">返回上一页</button>
`
})
export class NavigationComponent {
private router = inject(Router);
goHome() {
this.router.navigate(['/']);
}
goToUser() {
// 带参数导航
this.router.navigate(['/users', 123]);
// 带查询参数
this.router.navigate(['/users'], {
queryParams: { page: 1, size: 10 }
});
// 带片段(锚点)
this.router.navigate(['/users'], {
fragment: 'section-1'
});
// 相对导航
this.router.navigate(['../'], { relativeTo: this.route });
}
goBack() {
// 返回上一页
window.history.back();
}
}
路由事件
监听路由变化事件:
import { Component, inject } from '@angular/core';
import { Router, Event, NavigationStart, NavigationEnd, NavigationError } from '@angular/router';
@Component({
selector: 'app-root',
template: `
@if (loading) {
<div class="loading">加载中...</div>
}
<router-outlet></router-outlet>
`
})
export class AppComponent {
private router = inject(Router);
loading = false;
constructor() {
this.router.events.subscribe((event: Event) => {
if (event instanceof NavigationStart) {
this.loading = true;
}
if (event instanceof NavigationEnd) {
this.loading = false;
}
if (event instanceof NavigationError) {
this.loading = false;
console.error('导航错误:', event.error);
}
});
}
}
小结
- 路由配置使用
Routes数组定义路径和组件的映射 - router-outlet 是路由组件的渲染出口
- 路由参数包括路径参数和查询参数
- 子路由实现嵌套路由结构
- 路由守卫控制路由访问权限
- 懒加载优化应用加载性能
下一步
掌握了路由系统后,接下来学习 HTTP 客户端,了解如何与后端 API 交互。