理解
官方定义:在用户使用应用程序时,Angular 的路由器能让用户从一个视图导航到另一个视图。
使用
安装
需要先安装angular router
npm i --save @angular/router
base href
然后我们需要将 <base>
标签添加到我们的 index.html
文件中的<head>
标签里。路由需要根据这个来确定应用程序的根目录。
<!doctype html>
<html>
<head>
<base href="/">
<title>Application</title>
</head>
<body>
<app-root></app-root>
</body>
</html>
以上配置信息告诉 Angular 路由,应用程序的根目录是 /
。
导入
路由并不是 Angular 核心库的一部分,而是在它自己的 @angular/router
包中。
要使用路由,我们需要在app.module.ts
中,导入 RouterModule
。
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
配置
每个带路由的 Angular 应用都有一个Router(路由器)服务的单例对象。 当浏览器的 URL 变化时,路由器会查找对应的 Route(路由),并据此决定该显示哪个组件。
我们使用RouterModule.forRoot()
方法,在主模块中定义主要的路由信息,并把它的返回值添加到 AppModule
的 imports
数组中。
官方示例:
const appRoutes: Routes = [
{ path: 'crisis-center', component: CrisisListComponent },
{ path: 'hero/:id', component: HeroDetailComponent },
{
path: 'heroes',
component: HeroListComponent,
data: { title: 'Heroes List' }
},
{ path: '',
redirectTo: '/heroes',
pathMatch: 'full'
},
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(
appRoutes,
{ enableTracing: true } // <-- debugging purposes only
)
// other imports here
],
...
})
export class AppModule { }
每个 Route 都会把一个 URL 的 path 映射到一个组件。
注意,path 不能以斜杠(/)开头。 路由器会为解析和构建最终的 URL,这样当你在应用的多个视图之间导航时,可以任意使用相对路径和绝对路径。
第二个路由中的 :id 是一个路由参数的令牌(Token)。
第三个路由中的 data 属性用来存放于每个具体路由有关的任意信息。
第四个路由中的空路径(’’)表示应用的默认路径,当 URL 为空时就会访问那里,因此它通常会作为起点。
最后一个路由中的 ** 路径是一个通配符。当所请求的 URL 不匹配前面定义的路由表中的任何路径时,路由器就会选择此路由。 这个特性可用于显示“404 - Not Found”页,或自动重定向到其它路由。
这些路由的定义顺序是刻意如此设计的。路由器使用先匹配者优先的策略来匹配路由,所以,具体路由应该放在通用路由的前面。在上面的配置中,带静态路径的路由被放在了前面,后面是空路径路由,因此它会作为默认路由。而通配符路由被放在最后面,这是因为它能匹配上每一个 URL,因此应该只有在前面找不到其它能匹配的路由时才匹配它。
如果你想要看到在导航的生命周期中发生过哪些事件,可以使用路由器默认配置中的 enableTracing
选项。它会把每个导航生命周期中的事件输出到浏览器的控制台。 这应该只用于调试。你只需要把 enableTracing: true
选项作为第二个参数传给 RouterModule.forRoot()
方法就可以了。
还可以使用RouterModule.forChild()
,与forRoot()
方法类似,只不过它适用于特定的模块,我们不必在一个地方(我们的主模块)定义所有路由信息。反之,我们可以在特性模块中定义模块特有的路由信息,并在必要的时候将它们导入我们主模块。
路由出口
配置完路由信息后,下一步是使用一个名为 router-outlet
的指令告诉 Angular 在哪里加载组件。
如在 AppComponent
组件中,我们可以在任意位置插入 router-outlet
指令:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div class="app">
<h3>Our app</h3>
<router-outlet></router-outlet>
</div>
`
})
export class AppComponent {}
路由器链接
已经规定好了渲染路由的地方。之后在angular中,我们一般会用a标签的routerLink指令来导航到已经定义好的路由。
<h1>Angular Router</h1>
<nav>
<a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>
<a routerLink="/heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
RouterLinkActive
指令会基于当前的 RouterState
为活动的 RouterLink
切换所绑定的 css 类。
路由器状态
在导航时的每个生命周期成功完成时,路由器会构建出一个 ActivatedRoute
组成的树,它表示路由器的当前状态。 你可以在应用中的任何地方用 Router
服务及其 routerState
属性来访问当前的 RouterState
值。
RouterState
中的每个 ActivatedRoute
都提供了从任意激活路由开始向上或向下遍历路由树的一种方式,以获得关于父、子、兄弟路由的信息。
激活的路由
该路由的路径和参数可以通过注入进来的一个名叫ActivatedRoute
的路由服务来获取。 它有一大堆有用的信息,包括:
动态路由
基于动态路由我们可以根据不同的路由参数,渲染不同的页面。
例如,如果我们想要在个人资料页面根据不同的用户名显示不同的用户信息,我们可以使用以下方式定义路由:
import { HomeComponent } from './home/home.component';
import { ProfileComponent } from './profile/profile.component';
export const ROUTES: Routes = [
{ path: '', component: HomeComponent },
{ path: '/profile/:username', component: ProfileComponent }
];
这里的关键点是 :
,它告诉 Angular 路由,:username
是路由参数,而不是 URL 中实际的部分。
如果没有使用 : ,它将作为静态路由,仅匹配 /profile/username 路径
获取路由参数
要访问当前路由的相关信息,我们需要先从 @angular/router
模块中导入 ActivatedRoute
,然后在组件类的构造函数中注入该对象,最后通过订阅该对象的 params
属性,来获取路由参数,具体示例如下:
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
@Component({
selector: 'profile-page',
template: `
<div class="profile">
<h3>{{ username }}</h3>
</div>
`
})
export class SettingsComponent implements OnInit {
username: string;
constructor(private route: ActivatedRoute) {}
ngOnInit() {
this.route.params.subscribe((params) => this.username = params.username);
}
}
子路由
实际上每个路由都支持子路由,假设我们现在有一个 /settings
设置页面,下有 /settings/profile
和 /settings/password
两个页面,分别表示个人资料页和修改密码页。
我们可能希望我们的 / settings
页面拥有自己的组件,然后在设置页面组件中显示 /settings/profile
和 /settings/password
页面。我们可以这样做:
import { SettingsComponent } from './settings/settings.component';
import { ProfileSettingsComponent } from './settings/profile/profile.component';
import { PasswordSettingsComponent } from './settings/password/password.component';
export const ROUTES: Routes = [
{
path: 'settings',
component: SettingsComponent,
children: [
{ path: 'profile', component: ProfileSettingsComponent },
{ path: 'password', component: PasswordSettingsComponent }
]
}
];
@NgModule({
imports: [
BrowserModule,
RouterModule.forRoot(ROUTES)
],
})
export class AppModule {}
在这里,我们在 setttings
路由中定义了两个子路由,它们将继承父路由的路径,因此修改密码页面的路由匹配地址是 /settings/password
,依此类推。
接下来,我们需要做的最后一件事是在我们的 SettingsComponent
组件中添加 router-outlet
指令,因为我们要在设置页面中呈现子路由。
具体代码如下:
import { Component } from '@angular/core';
@Component({
selector: 'settings-page',
template: `
<div class="settings">
<settings-header></settings-header>
<settings-sidebar></settings-sidebar>
<router-outlet></router-outlet>
</div>
`
})
export class SettingsComponent {}