2.6路由 (Routing) 与导航 2.6 路由 (Routing) 与导航 (Navigation) 2.6.1 路由基础 Angular 的路由模块 负责管理应用的路由配置和导航。要使用路由,首先需要在应用中导入并配置 。 2.6.1.1 导入 RouterModule 在 中导入 并配置路由: 代码详解: : 导入路由模块和路由配置类型。 : 定义路由配置数组,每个对象定义一个路由规则。 : URL 路径。 : 与路径关联的组件。 : 重定向路径。 : 匹配策略, 表示完全匹配。 : 通配符,匹配所有未定义的路由。 : 配置根路由, 方法只能在根模块中使用。 2.6.1.2 使用 在 中使用 指令,它是一个占位符,用于显示与当前路由匹配的组件。
Angular 的路由模块 @angular/router 负责管理应用的路由配置和导航。要使用路由,首先需要在应用中导入并配置 RouterModule。
2.6.1.1 导入 RouterModule
在 app.module.ts 中导入 RouterModule 并配置路由:
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AppComponent } from './app.component'; import { HomeComponent } from './home/home.component'; import { AboutComponent } from './about/about.component'; const routes: Routes = [ { path: 'home', component: HomeComponent }, { path: 'about', component: AboutComponent }, { path: '', redirectTo: '/home', pathMatch: 'full' }, // 默认路由 { path: '**', redirectTo: '/home' } // 通配符路由 ]; @NgModule({ declarations: [ AppComponent, HomeComponent, AboutComponent ], imports: [ BrowserModule, RouterModule.forRoot(routes) // 配置根路由 ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
代码详解:
import { RouterModule, Routes } from '@angular/router';: 导入路由模块和路由配置类型。
const routes: Routes = [...]: 定义路由配置数组,每个对象定义一个路由规则。
path: URL 路径。
component: 与路径关联的组件。
redirectTo: 重定向路径。
pathMatch: 匹配策略,full 表示完全匹配。
**: 通配符,匹配所有未定义的路由。
RouterModule.forRoot(routes): 配置根路由,forRoot 方法只能在根模块中使用。
2.6.1.2 使用 <router-outlet>
在 app.component.html 中使用 <router-outlet> 指令,它是一个占位符,用于显示与当前路由匹配的组件。
<nav> <a routerLink="/home" routerLinkActive="active">Home</a> | <a routerLink="/about" routerLinkActive="active">About</a> </nav> <router-outlet></router-outlet>
代码详解:
<router-outlet>: Angular 会根据当前路由,将对应的组件渲染到这个位置。
routerLink: 指令用于创建导航链接,它会根据指定的路径生成 URL。
routerLinkActive: 指令用于在链接激活时添加 CSS 类名,用于样式标记。
2.6.1.3 组件创建
创建 HomeComponent 和 AboutComponent,用于显示不同的内容。
// home.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-home', template: `<h1>Home Page</h1>`, styleUrls: [] }) export class HomeComponent { } // about.component.ts import { Component } from '@angular/core'; @Component({ selector: 'app-about', template: `<h1>About Page</h1>`, styleUrls: [] }) export class AboutComponent { }
Angular 提供了两种主要的导航方式:声明式导航和编程式导航。
2.6.2.1 声明式导航
使用 routerLink 指令在模板中创建导航链接。这是一种简单且常用的导航方式。
<a routerLink="/home" routerLinkActive="active">Home</a>
2.6.2.2 编程式导航
使用 Router 服务的 navigate 方法在组件中进行导航。这提供了更灵活的导航控制,例如在事件处理函数中进行导航。
import { Component } from '@angular/core'; import { Router } from '@angular/router'; @Component({ selector: 'app-my-component', template: `<button (click)="goToAbout()">Go to About</button>` }) export class MyComponent { constructor(private router: Router) { } goToAbout() { this.router.navigate(['/about']); } }
代码详解:
import { Router } from '@angular/router';: 导入 Router 服务。
constructor(private router: Router): 通过依赖注入获取 Router 实例。
this.router.navigate(['/about']): 使用 navigate 方法导航到 /about 路径。 navigate 方法接受一个包含路径片段的数组。
路由参数允许我们将数据传递到路由中,并在目标组件中访问这些数据。
2.6.3.1 定义带参数的路由
在路由配置中定义带参数的路由,使用冒号 : 表示参数。
const routes: Routes = [ { path: 'product/:id', component: ProductComponent } ];
2.6.3.2 传递路由参数
在导航时传递路由参数。
声明式导航:
<a [routerLink]="['/product', productId]">Product Details</a>
编程式导航:
this.router.navigate(['/product', productId]);
2.6.3.3 获取路由参数
在目标组件中使用 ActivatedRoute 服务获取路由参数。
import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-product', template: `<h1>Product Details - ID: {{ productId }}</h1>` }) export class ProductComponent implements OnInit { productId: string | null = null; constructor(private route: ActivatedRoute) { } ngOnInit() { this.route.paramMap.subscribe(params => { this.productId = params.get('id'); }); } }
代码详解:
import { ActivatedRoute } from '@angular/router';: 导入 ActivatedRoute 服务。
constructor(private route: ActivatedRoute): 通过依赖注入获取 ActivatedRoute 实例。
this.route.paramMap.subscribe(params => ...): 订阅 paramMap observable,它包含路由参数。
params.get('id'): 获取名为 id 的路由参数。
查询参数是附加在 URL 结尾的参数,以 ? 开头,多个参数之间用 & 分隔。
2.6.4.1 传递查询参数
声明式导航:
<a [routerLink]="['/search']" [queryParams]="{ query: 'example' }">Search</a>
编程式导航:
this.router.navigate(['/search'], { queryParams: { query: 'example' } });
2.6.4.2 获取查询参数
在目标组件中使用 ActivatedRoute 服务获取查询参数。
import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; @Component({ selector: 'app-search', template: `<h1>Search Results for: {{ query }}</h1>` }) export class SearchComponent implements OnInit { query: string | null = null; constructor(private route: ActivatedRoute) { } ngOnInit() { this.route.queryParamMap.subscribe(params => { this.query = params.get('query'); }); } }
代码详解:
this.route.queryParamMap.subscribe(params => ...): 订阅 queryParamMap observable,它包含查询参数。
params.get('query'): 获取名为 query 的查询参数。
子路由允许我们将应用的不同部分组织成模块化的结构,每个模块都有自己的路由配置。
2.6.5.1 创建子模块
创建一个新的模块,例如 AdminModule,并配置其路由。
// admin.module.ts import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { RouterModule, Routes } from '@angular/router'; import { AdminDashboardComponent } from './admin-dashboard/admin-dashboard.component'; import { AdminUsersComponent } from './admin-users/admin-users.component'; const routes: Routes = [ { path: '', component: AdminDashboardComponent }, { path: 'users', component: AdminUsersComponent } ]; @NgModule({ declarations: [ AdminDashboardComponent, AdminUsersComponent ], imports: [ CommonModule, RouterModule.forChild(routes) // 配置子路由 ] }) export class AdminModule { }
代码详解:
RouterModule.forChild(routes): 配置子路由,forChild 方法只能在非根模块中使用。2.6.5.2 在根路由中配置子模块
在根模块的路由配置中,使用 loadChildren 属性加载子模块。
// app.module.ts import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { AppComponent } from './app.component'; import { HomeComponent } from './home/home.component'; import { AboutComponent } from './about/about.component'; const routes: Routes = [ { path: 'home', component: HomeComponent }, { path: 'about', component: AboutComponent }, { path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) }, // 加载子模块 { path: '', redirectTo: '/home', pathMatch: 'full' }, { path: '**', redirectTo: '/home' } ]; @NgModule({ declarations: [ AppComponent, HomeComponent, AboutComponent ], imports: [ BrowserModule, RouterModule.forRoot(routes) ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
代码详解:
loadChildren: 指定加载子模块的函数。 使用动态 import() 语法实现惰性加载。2.6.5.3 在父组件中使用 <router-outlet>
在 AdminDashboardComponent 的模板中使用 <router-outlet> 指令,用于显示子路由对应的组件。
<!-- admin-dashboard.component.html --> <h1>Admin Dashboard</h1> <nav> <a routerLink="users" routerLinkActive="active">Users</a> </nav> <router-outlet></router-outlet>
2.6.5.4 导航到子路由
使用 routerLink 指令或 Router 服务的 navigate 方法导航到子路由。
<!-- 在 AdminDashboardComponent 中 --> <a routerLink="users">Users</a> <!-- 或者在其他组件中 --> this.router.navigate(['/admin', 'users']);
惰性加载是一种优化技术,它允许我们在需要时才加载模块,而不是在应用启动时加载所有模块。这可以减少应用的初始加载时间,提高性能。
2.6.6.1 配置惰性加载
在根路由配置中,使用 loadChildren 属性加载子模块,并使用动态 import() 语法实现惰性加载。
const routes: Routes = [ { path: 'admin', loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule) } ];
2.6.6.2 验证惰性加载
可以使用浏览器的开发者工具(Network 选项卡)来验证模块是否被惰性加载。只有在导航到 admin 路由时,才会加载 admin.module.ts 及其依赖项。
路由守卫用于控制用户是否可以访问某个路由。Angular 提供了多种类型的路由守卫:
CanActivate: 决定是否可以激活一个路由。
CanActivateChild: 决定是否可以激活子路由。
CanDeactivate: 决定是否可以离开一个路由。
Resolve: 在路由激活之前获取数据。
CanLoad: 决定是否可以加载一个模块(用于惰性加载)。
2.6.7.1 创建路由守卫
使用 Angular CLI 创建一个路由守卫。
ng generate guard auth
2.6.7.2 实现 CanActivate 守卫
// auth.guard.ts import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; import { Observable } from 'rxjs'; import { AuthService } from './auth.service'; @Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate { constructor(private authService: AuthService, private router: Router) { } canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean { if (this.authService.isLoggedIn()) { return true; } else { this.router.navigate(['/login']); return false; } } }
代码详解:
implements CanActivate: 实现 CanActivate 接口。
canActivate 方法: 返回一个布尔值,指示是否允许激活路由。 如果用户已登录,则返回 true,否则重定向到登录页面并返回 false。
2.6.7.3 应用路由守卫
在路由配置中使用 canActivate 属性指定路由守卫。
const routes: Routes = [ { path: 'admin', component: AdminDashboardComponent, canActivate: [AuthGuard] } ];
2.6.7.4 AuthService (示例)
// auth.service.ts import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class AuthService { private loggedIn = false; login() { this.loggedIn = true; } logout() { this.loggedIn = false; } isLoggedIn(): boolean { return this.loggedIn; } }
Router 服务会触发一系列事件,例如 NavigationStart, NavigationEnd, NavigationError, RouteConfigLoadStart, RouteConfigLoadEnd 等。 可以订阅这些事件来执行自定义操作,例如显示加载指示器或记录导航历史。
import { Component } from '@angular/core'; import { Router, NavigationStart, NavigationEnd, NavigationError } from '@angular/router'; @Component({ selector: 'app-root', template: `...` }) export class AppComponent { loading = false; constructor(private router: Router) { this.router.events.subscribe(event => { if (event instanceof NavigationStart) { this.loading = true; } else if (event instanceof NavigationEnd || event instanceof NavigationError) { this.loading = false; } }); } }
代码详解:
this.router.events.subscribe(event => ...): 订阅 Router 服务的 events observable。
event instanceof NavigationStart: 检查事件是否为 NavigationStart 类型。
event instanceof NavigationEnd || event instanceof NavigationError: 检查事件是否为 NavigationEnd 或 NavigationError 类型。
Angular 的路由和导航功能强大且灵活,可以满足各种应用需求。通过本章节的学习,您应该掌握了以下技能:
配置路由和导航
传递和获取路由参数和查询参数
使用子路由组织应用结构
使用惰性加载优化性能
使用路由守卫控制访问权限
订阅路由事件执行自定义操作
掌握这些技能将帮助您构建更健壮、更易于维护的 Angular 应用。