跳到主要内容

路由系统

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);
}
});
}
}

小结

  1. 路由配置使用 Routes 数组定义路径和组件的映射
  2. router-outlet 是路由组件的渲染出口
  3. 路由参数包括路径参数和查询参数
  4. 子路由实现嵌套路由结构
  5. 路由守卫控制路由访问权限
  6. 懒加载优化应用加载性能

下一步

掌握了路由系统后,接下来学习 HTTP 客户端,了解如何与后端 API 交互。