Post

Angular V12 文档学习与整理(四) - 路由

创建

  1. 创建带路由的应用:

ng new routing-app --routing :

这个 –routing的意思就是立即为此 app 配置路由, 这会在应用的根模块(默认是AppModule)中添加一个路由模块(AppRoutingModule),并设置好基本的路由配置文件。

  1. 将路由模块导入到app.module
  2. app-routing.module 中声明 routes: Routes
1
2
3
4
const routes: Routes = [
  { path: 'first-component', component: FirstComponent },
  { path: 'second-component', component: SecondComponent },
];
  1. 将路由应用到项目
1
2
3
4
5
6
7
8
9
10
<h1>Angular Router App</h1>
<!-- This nav gives you links to click, which tells the router which route to use (defined in the routes constant in  AppRoutingModule) -->
<nav>
  <ul>
    <li><a routerLink="/first-component" routerLinkActive="active">First Component</a></li>
    <li><a routerLink="/second-component" routerLinkActive="active">Second Component</a></li>
  </ul>
</nav>
<!-- The routed views render in the <router-outlet>-->
<router-outlet></router-outlet>

RouterModule.forRoot方法用于在Angular应用的根模块中配置路由。这个方法接受一个路由定义数组(Routes)作为参数,并返回一个配置了路由的模块。

  • 参数:路由定义数组Routes。每个路由定义包含一个路径(path)、一个组件(component),以及可选的子路由(children)、路由守卫(canActivate)、懒加载模块(loadChildren)等。
  • 返回值:一个配置了路由的模块,这个模块应被导入到应用的根模块中。

路由的顺序

路由的顺序很重要,因为 Router 在匹配路由时使用“先到先得”策略,所以应该在不那么具体的路由前面放置更具体的路由。首先列出静态路径的路由,然后是一个与默认路由匹配的空路径路由。通配符路由是最后一个,因为它匹配每一个 URL,只有当其它路由都没有匹配时,Router 才会选择它。

路由传值

你可以使用 ActivatedRoute 接口, ActivatedRoute是一个服务,它提供了关于与当前路由有关的信息,比如路由参数、静态数据、URL等。

  • 获取路由参数:如果你的路由路径定义了参数(比如/product/:id),你可以使用ActivatedRoute来获取这些参数的值。
  • 获取查询参数和片段:你可以获取到URL的查询参数(即URL中?后面的部分)和片段(即URL中#后面的部分)。
  • 获取路由的静态数据:可以访问通过路由配置传递给路由的静态数据。
  • 观察路由和参数的变化ActivatedRoute提供了若干Observable对象,你可以订阅这些Observable以响应路由或参数的变化。
  1. ActivatedRouteParamMap 导入到组件。
  2. 通过把 ActivatedRoute 的一个实例添加到你的应用的构造函数中来注入它:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'app-product-detail',
  templateUrl: './product-detail.component.html',
  styleUrls: ['./product-detail.component.css']
})
export class ProductDetailComponent implements OnInit {
  id: string;

  constructor(private route: ActivatedRoute) { } // 注入 ActivatedRoute 实例

  ngOnInit(): void {
    // 使用ActivatedRoute
  }
}

  1. 获取参数的两种方式: 使用ActivatedRoutesnapshot属性或params Observable来获取路由参数。
1
2
3
4
5
6
7
8
9
10
11
12
13
// snapshot
ngOnInit(): void {
  this.id = this.route.snapshot.paramMap.get('id');
}


// params (Observable 对象)
ngOnInit(): void {
  this.route.params.subscribe(params => {
    this.id = params['id'];
  });
}

  1. 获取查询数据
1
2
3
4
5
6
7
8
9
10
11
// snapshot
ngOnInit(): void {
  let page = this.route.snapshot.queryParamMap.get('page');
}
// queryParams
ngOnInit(): void {
  this.route.queryParams.subscribe(queryParams => {
    let page = queryParams['page'];
  });
}

总结

ActivatedRoute是Angular路由系统的一部分,它允许你访问当前路由的信息,如参数、查询参数、URL片段等。

通配符路由

通配符路由(Wildcard Route)在Angular路由系统中用于匹配那些没有对应到任何其他路由的URL路径。它通常用作捕获所有未知或未处理的路由请求,比如显示一个404错误页面,或者将用户重定向到应用的某个默认页面, 以**的方式告诉 Angular 这是一个通配符路由。

1
2
3
4
5
6
const routes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: 'products', component: ProductsComponent },
  // 其他路由配置...
  { path: '**', component: PageNotFoundComponent } // 通配符路由,用于匹配所有未知的路径
];

路由重定向

要设置重定向,请使用重定向源的 path、要重定向目标的 component 和一个 pathMatch 值来配置路由,以告诉路由器该如何匹配 URL。

1
2
3
4
5
6
const routes: Routes = [
  { path: 'first-component', component: FirstComponent },
  { path: 'second-component', component: SecondComponent },
  { path: '',   redirectTo: '/first-component', pathMatch: 'full' }, // redirect to `first-component`
  { path: '**', component: PageNotFoundComponent },  // Wildcard route for a 404 page
];

编程式导航

1
2
3
4
goToItems() {
  this.router.navigate(['items'], { relativeTo: this.route });
}

查询查询参数与片段

在Web开发中,访问查询参数和片段(URL中的?key=value结构和#fragment部分)是一种获取从URL传递的数据的常见方式。

在Angular中,你可以通过ActivatedRoute服务来访问当前路由的查询参数和片段。ActivatedRoute提供了queryParamsfragment的Observable,你可以订阅这些Observable以响应URL的变化。

特性路由模块和主路由模块的结合

将特性路由模块与主路由模块结合起来,主要用于组织和划分应用的不同功能区域,提高模块化和可维护性。这通常涉及到特性模块的懒加载,从而实现按需加载特性模块,优化应用启动时间和性能。下面我将说明如何将特性路由模块与主路由模块结合起来,使用懒加载的方式。

1. 定义特性路由模块

首先,你需要创建一个特性模块并为其定义路由。这里以一个名为ProductsModule的特性模块为例,该模块包含产品列表的路由。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// products.module.ts
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule, Routes } from '@angular/router';
import { ProductListComponent } from './product-list/product-list.component';

const routes: Routes = [
  { path: '', component: ProductListComponent }
];

@NgModule({
  declarations: [ProductListComponent],
  imports: [
    CommonModule,
    RouterModule.forChild(routes)  // 使用forChild来定义特性路由
  ]
})
export class ProductsModule { }

2. 配置主路由模块以实现懒加载

在应用的主路由模块(通常是AppRoutingModule),你将通过特定的语法来配置懒加载的路由,指向你的特性模块。注意,路径中不再直接引用组件,而是使用loadChildren函数来指定模块文件和模块类名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// app-routing.module.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [
  {
    path: 'products',
    loadChildren: () => import('./products/products.module').then(m => m.ProductsModule)
  },
  // 其他路由配置...
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]  // 重新导出RouterModule,使其指令在AppModule中可用
})
export class AppRoutingModule { }

在这个例子中,当用户访问/products路径时,ProductsModule将会被懒加载。loadChildren方法接受一个箭头函数,这个函数动态导入特性模块,并返回一个Promise,该Promise解析为模块对象,然后使用.then(m => m.ProductsModule)来指定需要加载的模块。

3. 确保AppModule导入AppRoutingModule

最后,确保你的根模块AppModule已经导入了AppRoutingModule,这样主路由配置才会生效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule  // 主路由模块导入
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

通过这种方式,特性路由模块(和它们的路由)与主路由模块结合到一起,实现了功能区域的模块化和路由的懒加载。这种模式对于构建大型和性能优化的Angular应用至关重要。

路由守卫

路由守卫 (Route Guards) 是一种用来控制导航流程的机制, 可以在路由激活, 离开的时候执行某些逻辑, 可以用来执行权限检查, 预先加载组件数据, 以及判断是否离开当前页面

有四种类型的路由守卫:

  1. CanActivate:决定是否可以导航到某个路由。
  2. CanActivateChild:决定是否可以导航到某个子路由。
  3. CanDeactivate:决定是否可以离开当前路由。
  4. Resolve:在路由激活之前获取路由数据。
  5. CanLoad:决定是否可以异步加载某个特性模块。

要想创建一个守卫, 就要在一个服务里面实现守卫的接口

export class AuthGuard implements CanActivate {}

然后实现细节

1
2
3
4
5
6
7
8
9
10
11
12
13
  constructor(private router: Router) {}

  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {

    const isAuthenticated = // ... 检查用户是否认证
    if (!isAuthenticated) {
      this.router.navigate(['/login']); // 如果用户未认证,重定向到登录页面
      return false;
    }
    return true;
  }

进一步要去配置守卫:

1
2
3
4
5
6
7
const routes: Routes = [
  {
    path: 'secure',
    canActivate: [AuthGuard], // 在这里使用
    loadChildren: () => import('./secure/secure.module').then(m => m.SecureModule)
  }
];

CanActivateChild

  • 用途CanActivateChild用来决定是否可以导航某个父路由的子路由
  • 工作方式它在父路由层级上配置,当尝试导航到父路由的任何子路由时会被触发。与CanActivate相比,CanActivateChild提供了在父路由级别统一管理对子路由访问控制的能力

懒加载

loadChildren: () => import('./secure/secure.module').then(m => m.SecureModule)

这是Angular 实现懒加载的一种方式, 懒加载是一种性能优化技术, 允许应用按需加载特定的模块, 只有用户在实际需要访问的时候才会被加载。

  • loadChildren:这是Angular路由配置中的一个属性,用来指定一个模块懒加载的方式, loadChildren的值是一个返回模块类的Promise
  • () =>:这部分是一个箭头函数(Lambda表达式),它是JavaScript的一个特性,允许你定义匿名函数。这里它作为loadChildren的值,表示路由被访问时要执行的加载操作。
  • import('./secure/secure.module'):这是动态import语句,用于动态加载模块。这是一个JavaScript的提案,目前已经得到了广泛支持。它返回一个`Promise`对象,这个对象解析为一个包含所有模块导出的对象。
  • .then(m => m.SecureModule)import语句返回的Promise解析完成后,.then()方法会被调用,m代表模块导出的对象。这里我们从这个对象中获取SecureModule模块,并将它返回给Angular路由系统。

这种模式不仅适用于Angular,也是现代JavaScript应用中广泛使用的模块动态加载技术,它允许应用更加灵活地控制资源的加载

This post is licensed under CC BY 4.0 by the author.

Comments powered by Disqus.