
声明
这个教程的目标是让初学者快速掌握 Angular 路由的基本使用方法,避免复杂
的概念影响对路由基本用法的认识,因此只涉及路由模块导入、路由匹配算
法、路由跳转指令,并不涉及守护路由、子路由,解析路由(resolver),路
由参数的获取等高级路由知识 。
前言
路由是一个 Web 应用不可缺少的一环, 路由的存在可以让用户对应用中感兴
趣的内容进行收藏方便下次访问。 在单页应用程序(SPA)中,路由必须由前
端来处理。Angualr 提供了功能强大的路由模块,可以帮我们完成复杂的页
面路由需求。
对于一个正常的 Web 应用来说,当一个路由发生时,会触发两个方面的变化,
一是浏览器中的地址会发生改变,二是页面局部或者全部会发生改变。
为了做到这两点,Angular 通过监听浏览器地址的改变来触发页面的渲染。这
其中的关键要数 “占位符” 和路由匹配的用法!
那就让我们开始吧~
准备工作
为了代码的可维护性,我们遵循 Angular 的最佳实践,把路由作为一个单独的
模块独立出来。将其命名为 AppRoutingModule,在 AppModule 中导入。

接着,我们来编写这路由模块 AppRoutingModule 。同样为了简单起见,我
们先忽略路由匹配的具体内容,并用省略号代替。

可以看到,这里我们使用到了 @angular/router 包下的 RouterModule 模块
和内置的路由匹配数组 Routes。有别于其他 Module 的导入方式,这里我们用
RouterModule.forRoot 来生成一个可导入的 Module。
forRoot 与 forChild
为什么要使用 forRoot ?这是因为 forRoot 方法除了能为 AppModule 提供
Angular 内建路由组件和指令之外,还能在应用的整个生命周期提供一系列统
一的路由服务(Service)。这些服务在某些我们需要对路由进行更加复杂操作
的场合就会用到。而在子模块中,我们只需使用到路由的组件和指令,就不用
再次导入这些服务,因此此时只要使用 forChild 方法即可。

话说回来,为什么这些服务只要导入一次而不用导入多次?这是因为 Angular
用特殊的设计模式——依赖注入来满足我们的需求。依赖注入的特点就是,一
旦我们在代码中声明了这些服务的构造方式,Angular 就会在服务的作用域内
通过注入器生成一个单一的实例/对象。
假设我们在 AppModule 提供了一组服务,然后在其他模块也导入了这组服
务,并且我们将这些模块导入到 AppModule 中。那么就可能导致这组服务被
多次注册。虽然最后通过服务标记(其实叫 token)可以对服务实例进行覆
盖。但是这样相当于初始化了多次实例。性能上有所损耗。
更糟糕的是,在某些场景下(LazyLoad)就会非常神奇的拥有了多份完全不同
副本的实例,导致在资源共享的场景下出现 Bug。
我在这篇文章中详解了 forRoot 针对的场景。
延伸阅读
Angular Router 源码中 forRoot 提供的服务可是 forChild 的好多倍。可以
点击这里看路由模块 forRoot 方法提供的服务。
万事具备
好了~ 现在我们就来探索 Angular 是如何解决路由中 Where(在哪里渲染)和
How(如何渲染或者说用什么组件渲染)的问题的。
Where——占位符
Angular 在路由变化时如何渲染页面,是整体还是局部?
我们用一个占位符(placeholder)或者说是一个指令 router-outlet 来做到。

在视图层我们添加了这个占位符之后,一旦路由发生改变,Angular 就会利用
在路由匹配中定义的组件来对这个位置进行渲染,达到页面整体或局部变化的
目的。很简单吧! 接下来让我们来解决 How 的问题。
How——路由匹配
在 Angular 中,路由匹配用来解决如何渲染页面的问题。
我们知道,Angular 中,通过功能将各个页面和对应的逻辑划分成一个又一个
的组件。而所谓路由匹配,就是在浏览器地址(路由)发生变化时,Angular
通过预先维护的一组路由——组件映射,来对页面的整体或者局部进行渲染。

这边我们添加了三个路由匹配规则,profile、home 和 一个空路由。
这里的语法翻译成人类语言就是:
Profile 路由
路由前缀 profile 开头的路由,即形式为 localhost:4200/profie* 的路由,由组
件 ProfileComponent 负责渲染,替换掉 router-outlet 的位置。
Home 路由
路由前缀 home 开头的路由,即形式为 localhost:4200/home* 的路由,由组
件 HomeComponent 负责渲染,替换掉 router-outlet 的位置。
空路由
空路由,即形式为 localhost:4200/ 的路由 ,重定向到 /home。这里的重定向
意思是,把当前路由改成 home ,然后重新走一遍路由匹配表。
这里 redirectTo 就是重定向的意思。
细心的读者可能发现,这里对于空路由,除了 redirectTo 这条规则,我们还加
入了 pathMatch: full 这条规则。这是因为 Angular 中默认路由匹配的规则是
prefix(官方文档)。而空路由(”)默认是所有路由的前缀,所以,假如不加
full 的话,所有路由都会匹配到这条空路由上。这点切记。
当然,我在这篇详解路由匹配中对路由匹配机制有着更加详细的阐述,觉得不
过瘾的朋友可以看看。
最后一步!
所有的准备工作都已经完成!你就可以编写路由中涉及到的组件,来完成具体
的页面渲染逻辑了。我们只要在需要设置路由变化位置使用routerLink 指令就
能完成跳转啦。注意这里的 profile 要加上单引号哦,否则会被解析成组件的变
量而报错啦。

当然你也可以这样写,这样 profile 会被直接解析成字符串。

总结
路由的基本用法和一般的 Angular 内建模块有一些区别,主要在导入模块时需
要调用路由模块的 forRoot 方法来注入一些全局的服务。
同时针对路由的特点,Angular 利用 router-outlet 指令决定路由渲染 Where
的问题,利用路由匹配来解决 How 的问题。