Angular路由与导航
Angular路由系统允许在用户导航应用程序的不同视图之间进行切换,它支持路由参数、嵌套路由、路由守卫和延迟加载等高级功能。
核心价值
Angular路由优势
- 🧭 单页面应用:无需重新加载整个页面即可切换视图
- 🔗 URL管理:提供清晰可共享的URL,支持书签和历史
- 🧩 模块化路由:支持特性模块的路由分离
- 🔄 延迟加载:按需加载模块,提升初始加载性能
- 🛡️ 路由守卫:保护路由,实现权限控制
1. 路由基础
1.1 配置路由
在Angular应用中配置路由需要以下步骤:
- 创建路由模块:
app-routing.module.ts
typescript
1import { NgModule } from '@angular/core';2import { RouterModule, Routes } from '@angular/router';34import { HomeComponent } from './home/home.component';5import { AboutComponent } from './about/about.component';6import { ContactComponent } from './contact/contact.component';7import { NotFoundComponent } from './not-found/not-found.component';89const routes: Routes = [10 { path: '', component: HomeComponent },11 { path: 'home', redirectTo: '', pathMatch: 'full' },12 { path: 'about', component: AboutComponent },13 { path: 'contact', component: ContactComponent },14 { path: '**', component: NotFoundComponent } // 通配符路由必须放在最后15];1617@NgModule({18 imports: [RouterModule.forRoot(routes)],19 exports: [RouterModule]20})21export class AppRoutingModule { }- 在主模块中导入路由模块:
app.module.ts
typescript
1import { NgModule } from '@angular/core';2import { BrowserModule } from '@angular/platform-browser';3import { AppRoutingModule } from './app-routing.module';4import { AppComponent } from './app.component';5// 导入组件6import { HomeComponent } from './home/home.component';7import { AboutComponent } from './about/about.component';8import { ContactComponent } from './contact/contact.component';9import { NotFoundComponent } from './not-found/not-found.component';1011@NgModule({12 declarations: [13 AppComponent,14 HomeComponent,15 AboutComponent,16 ContactComponent,17 NotFoundComponent18 ],19 imports: [20 BrowserModule,21 AppRoutingModule22 ],23 providers: [],24 bootstrap: [AppComponent]25})26export class AppModule { }- 添加路由出口和导航链接:
app.component.html
html
1<header>2 <nav>3 <ul>4 <li><a routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">首页</a></li>5 <li><a routerLink="/about" routerLinkActive="active">关于我们</a></li>6 <li><a routerLink="/contact" routerLinkActive="active">联系我们</a></li>7 </ul>8 </nav>9</header>1011<main>12 <!-- 路由出口 - 匹配的组件将显示在这里 -->13 <router-outlet></router-outlet>14</main>1516<footer>17 <p>© 2025 Angular路由示例</p>18</footer>1.2 路由配置选项
Angular路由支持多种配置选项:
typescript
1const routes: Routes = [2 { 3 path: 'products', 4 component: ProductListComponent,5 children: [ // 嵌套路由6 { 7 path: ':id', 8 component: ProductDetailComponent 9 }10 ],11 canActivate: [AuthGuard], // 路由守卫12 data: { title: '产品列表', permissions: ['view-products'] }, // 路由数据13 resolve: { // 预先解析数据14 products: ProductsResolver15 }16 },17 { 18 path: 'admin', 19 loadChildren: () => import('./admin/admin.module')20 .then(m => m.AdminModule) // 懒加载模块21 }22];1.3 Router相关指令
Angular路由系统提供了几个重要的指令:
- RouterOutlet:
<router-outlet></router-outlet>- 显示匹配的组件 - RouterLink:
<a routerLink="/path">- 声明式导航链接 - RouterLinkActive:
<a routerLinkActive="active">- 当链接激活时应用的CSS类 - RouterLinkActiveOptions:
[routerLinkActiveOptions]="{exact: true}"- 配置激活行为
1.4 程序式导航
除了使用指令进行导航外,还可以通过Router服务进行程序式导航:
typescript
1import { Component } from '@angular/core';2import { Router, ActivatedRoute } from '@angular/router';34@Component({5 selector: 'app-navigation-demo',6 template: `7 <button (click)="goToProducts()">查看产品</button>8 <button (click)="goToProductDetail(123)">查看产品详情</button>9 <button (click)="goBack()">返回</button>10 `11})12export class NavigationDemoComponent {13 constructor(14 private router: Router,15 private route: ActivatedRoute16 ) {}17 18 goToProducts() {19 this.router.navigate(['/products']);20 }21 22 goToProductDetail(id: number) {23 // 相对于当前路由的导航24 this.router.navigate([id], { relativeTo: this.route });25 26 // 或使用绝对路径27 // this.router.navigate(['/products', id]);28 }29 30 goToProductWithQueryParams(category: string) {31 this.router.navigate(['/products'], { 32 queryParams: { category: category },33 queryParamsHandling: 'merge' // 合并现有查询参数34 });35 }36 37 goBack() {38 window.history.back();39 }40}2. 路由参数与数据传递
2.1 路由参数
Angular路由支持三种参数传递方式:
- 路径参数:作为URL路径的一部分
- 查询参数:作为URL查询字符串的一部分
- 路由数据:在路由配置中定义的静态数据
路径参数示例
app-routing.module.ts
typescript
1const routes: Routes = [2 { 3 path: 'products/:id', 4 component: ProductDetailComponent 5 }6];product-detail.component.ts
typescript
1import { Component, OnInit } from '@angular/core';2import { ActivatedRoute, ParamMap } from '@angular/router';3import { switchMap } from 'rxjs/operators';4import { ProductService } from '../services/product.service';5import { Observable } from 'rxjs';6import { Product } from '../models/product.model';78@Component({9 selector: 'app-product-detail',10 template: `11 <div *ngIf="product$ | async as product; else loading">12 <h2>{{ product.name }}</h2>13 <p>{{ product.description }}</p>14 <p>价格: {{ product.price | currency:'CNY' }}</p>15 </div>16 <ng-template #loading>加载中...</ng-template>17 `18})19export class ProductDetailComponent implements OnInit {20 product$: Observable<Product>;21 22 constructor(23 private route: ActivatedRoute,24 private productService: ProductService25 ) {}26 27 ngOnInit() {28 // 方式1:使用快照(仅适用于组件不会重用的情况)29 // const id = this.route.snapshot.paramMap.get('id');30 // this.product$ = this.productService.getProduct(id);31 32 // 方式2:使用可观察对象(适用于组件重用的情况)33 this.product$ = this.route.paramMap.pipe(34 switchMap((params: ParamMap) => {35 const id = params.get('id');36 return this.productService.getProduct(id);37 })38 );39 }40}查询参数示例
product-list.component.ts
typescript
1import { Component, OnInit } from '@angular/core';2import { ActivatedRoute, Router } from '@angular/router';3import { ProductService } from '../services/product.service';4import { Product } from '../models/product.model';56@Component({7 selector: 'app-product-list',8 template: `9 <div class="filters">10 <button (click)="filterByCategory('electronics')">电子产品</button>11 <button (click)="filterByCategory('clothing')">服装</button>12 <button (click)="filterByCategory('books')">图书</button>13 <button (click)="clearFilters()">清除筛选</button>14 </div>15 16 <div class="product-list">17 <div *ngFor="let product of products" class="product-item">18 <h3>{{ product.name }}</h3>19 <p>{{ product.description }}</p>20 <p>{{ product.price | currency:'CNY' }}</p>21 <button (click)="viewDetails(product.id)">查看详情</button>22 </div>23 </div>24 `25})26export class ProductListComponent implements OnInit {27 products: Product[] = [];28 29 constructor(30 private route: ActivatedRoute,31 private router: Router,32 private productService: ProductService33 ) {}34 35 ngOnInit() {36 this.route.queryParams.subscribe(params => {37 const category = params['category'];38 if (category) {39 this.loadProductsByCategory(category);40 } else {41 this.loadAllProducts();42 }43 });44 }45 46 filterByCategory(category: string) {47 this.router.navigate([], {48 relativeTo: this.route,49 queryParams: { category: category },50 queryParamsHandling: 'merge'51 });52 }53 54 clearFilters() {55 this.router.navigate([], {56 relativeTo: this.route,57 queryParams: {}58 });59 }60 61 viewDetails(id: string) {62 this.router.navigate(['/products', id]);63 }64 65 private loadAllProducts() {66 this.productService.getProducts().subscribe(products => {67 this.products = products;68 });69 }70 71 private loadProductsByCategory(category: string) {72 this.productService.getProductsByCategory(category).subscribe(products => {73 this.products = products;74 });75 }76}路由数据示例
app-routing.module.ts
typescript
1const routes: Routes = [2 { 3 path: 'products', 4 component: ProductListComponent,5 data: { 6 title: '产品列表', 7 breadcrumb: '产品',8 permissions: ['view-products']9 }10 }11];product-list.component.ts
typescript
1ngOnInit() {2 // 访问静态路由数据3 this.route.data.subscribe(data => {4 this.pageTitle = data['title'];5 document.title = this.pageTitle; // 更新页面标题6 7 // 检查权限8 const permissions = data['permissions'];9 this.canEdit = this.authService.hasPermissions(permissions);10 });11}2.2 路由解析器(Resolver)
路由解析器允许在激活路由之前获取数据,避免在组件加载后显示空白状态或加载指示器:
product-resolver.service.ts
typescript
1import { Injectable } from '@angular/core';2import { Resolve, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';3import { Observable, of } from 'rxjs';4import { catchError } from 'rxjs/operators';5import { ProductService } from './product.service';6import { Product } from '../models/product.model';78@Injectable({9 providedIn: 'root'10})11export class ProductResolver implements Resolve<Product> {12 constructor(private productService: ProductService) {}13 14 resolve(15 route: ActivatedRouteSnapshot,16 state: RouterStateSnapshot17 ): Observable<Product> {18 const id = route.paramMap.get('id');19 return this.productService.getProduct(id).pipe(20 catchError(error => {21 console.error('产品数据获取失败', error);22 // 返回默认产品或空对象23 return of({ id: null, name: '未找到产品', description: '', price: 0 });24 })25 );26 }27}app-routing.module.ts
typescript
1const routes: Routes = [2 { 3 path: 'products/:id', 4 component: ProductDetailComponent,5 resolve: {6 product: ProductResolver7 }8 }9];product-detail.component.ts
typescript
1ngOnInit() {2 // 使用解析器提供的数据,无需等待3 this.route.data.subscribe(data => {4 this.product = data['product'];5 });6}3. 嵌套路由与子路由
3.1 嵌套路由配置
嵌套路由允许创建更复杂的应用程序结构,其中子路由与父路由相关:
app-routing.module.ts
typescript
1const routes: Routes = [2 { 3 path: 'admin', 4 component: AdminComponent,5 children: [6 { path: '', redirectTo: 'dashboard', pathMatch: 'full' },7 { path: 'dashboard', component: AdminDashboardComponent },8 { path: 'users', component: UserManagementComponent },9 { path: 'products', component: ProductManagementComponent }10 ]11 }12];admin.component.html
html
1<div class="admin-layout">2 <aside class="sidebar">3 <nav>4 <ul>5 <li><a routerLink="/admin/dashboard" routerLinkActive="active">仪表盘</a></li>6 <li><a routerLink="/admin/users" routerLinkActive="active">用户管理</a></li>7 <li><a routerLink="/admin/products" routerLinkActive="active">产品管理</a></li>8 </ul>9 </nav>10 </aside>11 12 <main class="content">13 <!-- 子路由将在这里渲染 -->14 <router-outlet></router-outlet>15 </main>16</div>3.2 命名路由出口
Angular支持多个命名的路由出口,允许在同一视图中同时显示多个路由组件:
app-routing.module.ts
typescript
1const routes: Routes = [2 {3 path: 'workspace',4 component: WorkspaceComponent,5 children: [6 { 7 path: ':id',8 component: WorkspaceContentComponent,9 children: [10 { path: '', component: WorkspaceDetailComponent, outlet: 'detail' },11 { path: '', component: WorkspacePropertiesComponent, outlet: 'properties' }12 ]13 }14 ]15 }16];workspace.component.html
html
1<div class="workspace-layout">2 <aside class="sidebar">3 <app-workspace-list></app-workspace-list>4 </aside>5 6 <main class="main-content">7 <!-- 主路由出口 -->8 <router-outlet></router-outlet>9 </main>10</div>workspace-content.component.html
html
1<div class="content-container">2 <div class="detail-panel">3 <!-- 命名为"detail"的路由出口 -->4 <router-outlet name="detail"></router-outlet>5 </div>6 7 <div class="properties-panel">8 <!-- 命名为"properties"的路由出口 -->9 <router-outlet name="properties"></router-outlet>10 </div>11</div>3.3 相对导航
在嵌套路由结构中,相对导航非常有用:
typescript
1// 导航到当前路由的子路由2this.router.navigate(['child'], { relativeTo: this.route });34// 导航到父路由5this.router.navigate(['../'], { relativeTo: this.route });67// 导航到兄弟路由8this.router.navigate(['../sibling'], { relativeTo: this.route });4. 路由守卫
路由守卫是拦截导航过程并决定是否允许导航继续的服务。Angular提供了多种类型的守卫:
4.1 CanActivate守卫
控制是否可以访问路由:
auth.guard.ts
typescript
1import { Injectable } from '@angular/core';2import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';3import { Observable } from 'rxjs';4import { AuthService } from './auth.service';56@Injectable({7 providedIn: 'root'8})9export class AuthGuard implements CanActivate {10 constructor(private authService: AuthService, private router: Router) {}11 12 canActivate(13 route: ActivatedRouteSnapshot,14 state: RouterStateSnapshot15 ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {16 if (this.authService.isLoggedIn()) {17 // 检查特定权限18 const requiredPermissions = route.data['permissions'] as string[];19 if (requiredPermissions && !this.authService.hasPermissions(requiredPermissions)) {20 // 重定向到权限不足页面21 return this.router.createUrlTree(['/forbidden']);22 }23 24 return true;25 }26 27 // 保存原始URL,登录后重定向回来28 return this.router.createUrlTree(['/login'], { 29 queryParams: { returnUrl: state.url } 30 });31 }32}4.2 CanActivateChild守卫
控制是否可以访问子路由:
admin.guard.ts
typescript
1import { Injectable } from '@angular/core';2import { CanActivateChild, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';3import { Observable } from 'rxjs';4import { AuthService } from './auth.service';56@Injectable({7 providedIn: 'root'8})9export class AdminGuard implements CanActivateChild {10 constructor(private authService: AuthService, private router: Router) {}11 12 canActivateChild(13 childRoute: ActivatedRouteSnapshot,14 state: RouterStateSnapshot15 ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {16 if (this.authService.isAdmin()) {17 return true;18 }19 20 return this.router.createUrlTree(['/forbidden']);21 }22}4.3 CanDeactivate守卫
控制是否可以离开当前路由,通常用于防止用户意外丢失未保存的更改:
can-deactivate.guard.ts
typescript
1import { Injectable } from '@angular/core';2import { CanDeactivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';3import { Observable } from 'rxjs';45export interface CanComponentDeactivate {6 canDeactivate: () => Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree;7}89@Injectable({10 providedIn: 'root'11})12export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {13 canDeactivate(14 component: CanComponentDeactivate,15 currentRoute: ActivatedRouteSnapshot,16 currentState: RouterStateSnapshot,17 nextState?: RouterStateSnapshot18 ): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {19 return component.canDeactivate ? component.canDeactivate() : true;20 }21}product-edit.component.ts
typescript
1import { Component, OnInit } from '@angular/core';2import { FormBuilder, FormGroup, Validators } from '@angular/forms';3import { CanComponentDeactivate } from '../can-deactivate.guard';4import { Observable } from 'rxjs';56@Component({7 selector: 'app-product-edit',8 templateUrl: './product-edit.component.html'9})10export class ProductEditComponent implements OnInit, CanComponentDeactivate {11 productForm: FormGroup;12 originalProduct: any;13 hasUnsavedChanges = false;14 15 constructor(private fb: FormBuilder) {}16 17 ngOnInit() {18 this.productForm = this.fb.group({19 name: ['', Validators.required],20 price: [0, [Validators.required, Validators.min(0)]],21 description: ['']22 });23 24 // 监听表单更改25 this.productForm.valueChanges.subscribe(() => {26 this.hasUnsavedChanges = this.productForm.dirty;27 });28 29 // 获取原始产品数据...30 }31 32 // 实现CanComponentDeactivate接口33 canDeactivate(): Observable<boolean> | Promise<boolean> | boolean {34 if (!this.hasUnsavedChanges) {35 return true;36 }37 38 // 弹出确认对话框39 return window.confirm('您有未保存的更改,确定要离开吗?');40 }41 42 saveChanges() {43 // 保存表单数据...44 this.hasUnsavedChanges = false;45 }46}4.4 CanLoad守卫
控制是否可以加载延迟加载模块:
auth.guard.ts
typescript
1import { Injectable } from '@angular/core';2import { CanLoad, Route, UrlSegment, Router } from '@angular/router';3import { Observable } from 'rxjs';4import { AuthService } from './auth.service';56@Injectable({7 providedIn: 'root'8})9export class AuthGuard implements CanLoad {10 constructor(private authService: AuthService, private router: Router) {}11 12 canLoad(13 route: Route,14 segments: UrlSegment[]15 ): Observable<boolean> | Promise<boolean> | boolean {16 if (this.authService.isLoggedIn()) {17 return true;18 }19 20 // 未登录,导航到登录页面21 this.router.navigate(['/login']);22 return false;23 }24}4.5 Resolve守卫
前面已经介绍过,用于在路由激活前预先获取数据。
5. 延迟加载
延迟加载是Angular路由系统的一个强大特性,它允许将应用程序拆分成多个模块,并在需要时按需加载,这可以显著提高初始加载性能。
5.1 配置延迟加载模块
app-routing.module.ts
typescript
1const routes: Routes = [2 { path: '', component: HomeComponent },3 { 4 path: 'products', 5 loadChildren: () => import('./products/products.module').then(m => m.ProductsModule),6 canLoad: [AuthGuard]7 },8 { 9 path: 'admin', 10 loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule),11 canLoad: [AdminGuard]12 }13];5.2 延迟加载模块配置
每个延迟加载的模块必须有自己的路由配置:
products/products-routing.module.ts
typescript
1import { NgModule } from '@angular/core';2import { RouterModule, Routes } from '@angular/router';34import { ProductListComponent } from './product-list.component';5import { ProductDetailComponent } from './product-detail.component';6import { ProductEditComponent } from './product-edit.component';7import { ProductResolver } from './product.resolver';8import { CanDeactivateGuard } from '../can-deactivate.guard';910const routes: Routes = [11 { path: '', component: ProductListComponent },12 { 13 path: ':id', 14 component: ProductDetailComponent,15 resolve: { product: ProductResolver }16 },17 { 18 path: ':id/edit', 19 component: ProductEditComponent,20 canDeactivate: [CanDeactivateGuard]21 }22];2324@NgModule({25 imports: [RouterModule.forChild(routes)],26 exports: [RouterModule]27})28export class ProductsRoutingModule { }products/products.module.ts
typescript
1import { NgModule } from '@angular/core';2import { CommonModule } from '@angular/common';3import { ReactiveFormsModule } from '@angular/forms';45import { ProductsRoutingModule } from './products-routing.module';6import { ProductListComponent } from './product-list.component';7import { ProductDetailComponent } from './product-detail.component';8import { ProductEditComponent } from './product-edit.component';910@NgModule({11 declarations: [12 ProductListComponent,13 ProductDetailComponent,14 ProductEditComponent15 ],16 imports: [17 CommonModule,18 ReactiveFormsModule,19 ProductsRoutingModule20 ]21})22export class ProductsModule { }5.3 预加载策略
Angular路由系统支持多种预加载策略:
app-routing.module.ts
typescript
1import { PreloadAllModules, NoPreloading } from '@angular/router';23@NgModule({4 imports: [5 RouterModule.forRoot(routes, {6 preloadingStrategy: PreloadAllModules, // 预加载所有延迟加载模块7 // 或者不预加载8 // preloadingStrategy: NoPreloading9 })10 ],11 exports: [RouterModule]12})13export class AppRoutingModule { }也可以创建自定义的预加载策略:
selective-preloading-strategy.service.ts
typescript
1import { Injectable } from '@angular/core';2import { PreloadingStrategy, Route } from '@angular/router';3import { Observable, of } from 'rxjs';45@Injectable({6 providedIn: 'root'7})8export class SelectivePreloadingStrategy implements PreloadingStrategy {9 preload(route: Route, load: () => Observable<any>): Observable<any> {10 if (route.data && route.data['preload']) {11 return load();12 }13 return of(null);14 }15}app-routing.module.ts
typescript
1const routes: Routes = [2 { 3 path: 'products', 4 loadChildren: () => import('./products/products.module').then(m => m.ProductsModule),5 data: { preload: true } // 标记此模块进行预加载6 },7 { 8 path: 'admin', 9 loadChildren: () => import('./admin/admin.module').then(m => m.AdminModule)10 // 不预加载11 }12];1314@NgModule({15 imports: [16 RouterModule.forRoot(routes, {17 preloadingStrategy: SelectivePreloadingStrategy18 })19 ],20 exports: [RouterModule]21})22export class AppRoutingModule { }6. 路由事件与生命周期
Angular路由系统触发一系列的事件,可用于跟踪路由过程并实现加载指示器或分析跟踪:
app.component.ts
typescript
1import { Component, OnInit } from '@angular/core';2import { Router, NavigationStart, NavigationEnd, NavigationCancel, NavigationError, Event } from '@angular/router';3import { filter } from 'rxjs/operators';45@Component({6 selector: 'app-root',7 template: `8 <div class="loading-indicator" *ngIf="loading">加载中...</div>9 <router-outlet></router-outlet>10 `11})12export class AppComponent implements OnInit {13 loading = false;14 15 constructor(private router: Router) {}16 17 ngOnInit() {18 this.router.events.subscribe((event: Event) => {19 switch (true) {20 case event instanceof NavigationStart:21 this.loading = true;22 break;23 24 case event instanceof NavigationEnd:25 case event instanceof NavigationCancel:26 case event instanceof NavigationError:27 this.loading = false;28 break;29 }30 });31 32 // 页面标题更新33 this.router.events.pipe(34 filter(event => event instanceof NavigationEnd)35 ).subscribe(() => {36 const rt = this.getChild(this.router.routerState.root);37 rt.data.subscribe(data => {38 // 更新页面标题39 if (data.title) {40 document.title = `${data.title} - My App`;41 }42 });43 });44 }45 46 // 递归获取活动路由47 private getChild(activatedRoute: ActivatedRoute): ActivatedRoute {48 if (activatedRoute.firstChild) {49 return this.getChild(activatedRoute.firstChild);50 } else {51 return activatedRoute;52 }53 }54}7. 路由最佳实践
7.1 路由组织模式
对于大型应用,建议采用以下路由组织模式:
- 模块化路由:每个功能模块有自己的路由配置
- 基于特性的组织:按特性或业务领域组织路由
- 懒加载与预加载:合理使用懒加载和预加载策略
7.2 路由命名约定
使用一致的路由命名约定有助于提高可维护性:
- 使用kebab-case:例如
/user-profile而不是/userProfile - 资源标识:对于RESTful风格的路由,使用
/resources/:id/action格式 - 避免动词:使用名词命名路由,例如
/products而不是/get-products
7.3 路由安全
实施多层路由安全策略:
- 认证守卫:验证用户是否已登录
- 授权守卫:验证用户是否有权访问特定资源
- 数据验证:在服务器端和客户端验证路由参数
- CSRF保护:防止跨站请求伪造攻击
7.4 路由状态管理
有效管理路由状态:
- 使用查询参数:存储过滤、排序、分页等UI状态
- 路由参数序列化:对复杂的查询参数进行序列化
- 保持URL清晰:URL应该是可读的,可分享的,可书签的
7.5 路由性能优化
提高路由性能:
- 延迟加载:按需加载模块
- 路由预加载:在空闲时预加载常用模块
- 数据缓存:缓存路由解析器获取的数据
- Route Reuse策略:实现自定义路由复用策略,避免不必要的组件重建
custom-route-reuse-strategy.ts
typescript
1import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy } from '@angular/router';23export class CustomRouteReuseStrategy implements RouteReuseStrategy {4 private storedRoutes = new Map<string, DetachedRouteHandle>();5 6 // 确定是否复用路由7 shouldDetach(route: ActivatedRouteSnapshot): boolean {8 // 只存储标记为可复用的路由9 return route.data && route.data['reuseRoute'];10 }11 12 // 存储分离的路由13 store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {14 const id = this.getRouteId(route);15 this.storedRoutes.set(id, handle);16 }17 18 // 确定是否有存储的路由19 shouldAttach(route: ActivatedRouteSnapshot): boolean {20 const id = this.getRouteId(route);21 return this.storedRoutes.has(id);22 }23 24 // 恢复存储的路由25 retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {26 const id = this.getRouteId(route);27 if (!this.storedRoutes.has(id)) return null;28 return this.storedRoutes.get(id);29 }30 31 // 确定是否应该复用路由32 shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {33 // 默认的复用策略34 return future.routeConfig === curr.routeConfig;35 }36 37 // 获取路由ID38 private getRouteId(route: ActivatedRouteSnapshot): string {39 // 创建一个唯一的路由标识符,考虑路径和参数40 const path = route.routeConfig ? route.routeConfig.path : '';41 const id = path || '';42 43 // 如果有路由参数,将其添加到ID中44 if (route.params) {45 Object.keys(route.params).forEach(key => {46 id += `|${key}=${route.params[key]}`;47 });48 }49 50 return id;51 }52}app.module.ts
typescript
1import { BrowserModule } from '@angular/platform-browser';2import { NgModule } from '@angular/core';3import { RouteReuseStrategy } from '@angular/router';4import { AppRoutingModule } from './app-routing.module';5import { AppComponent } from './app.component';6import { CustomRouteReuseStrategy } from './custom-route-reuse-strategy';78@NgModule({9 declarations: [AppComponent],10 imports: [BrowserModule, AppRoutingModule],11 providers: [12 { provide: RouteReuseStrategy, useClass: CustomRouteReuseStrategy }13 ],14 bootstrap: [AppComponent]15})16export class AppModule { }
评论