导航是很简单的,只是不同页面之间的切换,路由是实现导航的一种。
一个url对应的一个页面,在angular2中是一个组件。定义一个规则。
设计的时候,先去
基础知识
- <base href> 大多数带路由的应用都要在index.html的<head>标签下先添加一个<base>元素,来告诉路由器该如何合成导航用的URL。
- 路由是从
@angular/router
包中引入的。 - 路由都是需要进行配置的。而这个配置需要的也就是
RouterModule
模块。 - 一个路由配置
- path中不能用斜线/开头。
- <u>这些路由的定义顺序是故意如此设计的。</u> 路由器使用先匹配者优先的策略来匹配路由,所以,具体路由应该放在通用路由的前面。在上面的配置中,带静态路径的路由被放在了前面,后面是空路径路由,因此它会作为默认路由。而通配符路由被放在最后面,这是因为它是最通用的路由,应该只在前面找不到其它能匹配的路由时才匹配它。
const appRoutes: Routes = [
{
path:'',// empty path匹配各级路由的默认路径。 它还支持在不扩展URL路径的前提下添加路由。
component: DashboardComponent
},{
path: 'dashboard',
component: DashboardComponent
}, {
path: 'loopback',
component: LoopbackComponent
}, {
path: 'heroparent',
component: HeroParentComponent
}, {
path:'version',
component: VersionParentComponent
}, {
path: '**',// **代表该路由是一个通配符路径。如果当前URL无法匹配上我们配置过的任何一个路由中的路径,路由器就会匹配上这一个。当需要显示404页面或者重定向到其它路由时,该特性非常有用。
component: DashboardComponent,
}
];
export const appRoutingModule: ModuleWithProviders = RouterModule.forRoot(appRoutes);
- RouterOutlet - 路由插座 显示路由器生成的视图。在展示父路由的位置中的某个地方展示子路由对应的地方。
路由模块
最开始的路由,我们是直接写在app.module.ts
文件中的,像这样,我们可以实现简单的导航。
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { RouterModule, Routes } from '@angular/router';
import { AppComponent } from './app.component';
import { CrisisListComponent } from './crisis-list.component';
import { HeroListComponent } from './hero-list.component';
const appRoutes: Routes = [
{ path: 'crisis-center', component: CrisisListComponent },
{ path: 'heroes', component: HeroListComponent }
];
@NgModule({
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot(appRoutes)
],
declarations: [
AppComponent,
HeroListComponent,
CrisisListComponent
],
bootstrap: [ AppComponent ]
})
export class AppModule {
}
但是这样不方便,所以我们要进行路由的分离,重构成我们自己的路由模块。like this:
代码语言:javascript复制const appRoutes: Routes = [
{
path:'',
component: DashboardComponent
},{
path: 'dashboard',
component: DashboardComponent
}, {
path: 'loopback',
component: LoopbackComponent
}, {
path: 'heroparent',
component: HeroParentComponent
}, {
path:'version',
component: VersionParentComponent
}, {
path: '**',
component: DashboardComponent,
}
];
export const appRoutingModule: ModuleWithProviders = RouterModule.forRoot(appRoutes);
同样我们还可以写多个路由模块。但是我们必须在app.module.ts
中进行imports:[appRoutingModule]
。
组件路由
我们需要将一些特征区域分割开来,做成自己单独的模块。必如hero模块。在这里,我们需要hero单独的导航,这也就是组件路由。 平级的路由
代码语言:javascript复制@NgModule({
imports: [
RouterModule.forChild([
{ path: 'heroes', component: HeroListComponent },
{ path: 'hero/:id', component: HeroDetailComponent },
{ path:'heroform', component: HeroFormComponent },
])
],
exports: [
RouterModule
]
})
export class HeroRoutingModule { }
我们还有另外一中类型的路由组织方式,路由树的形式。
代码语言:javascript复制const crisisCenterRoutes: Routes = [
{
path: '',
redirectTo: '/crisis-center',
pathMatch: 'full'
}, {
path: 'crisis-center',
component: CrisisCenterComponent,
children: [
{
path: '',
component: CrisisListComponent,
children: [
{
path: ':id',
component: CrisisDetailComponent,
},
{
path: '',
component: CrisisCenterHomeComponent
}
]
}
]
}
];
@NgModule({
imports: [
RouterModule.forChild(crisisCenterRoutes)
],
exports: [
RouterModule
]
})
export class CrisisCenterRoutingModule { }
- 重定向路由 添加一个redirect路由,它会把初始的相对URL('')悄悄翻译成默认路径(/crisis-center)。
{
path: '',
redirectTo: '/crisis-center',
pathMatch: 'full'
},
路由守卫
简介
路由守卫,应用在这个路由不是对所有导航都有效的,是有一些前置条件的,只有当这些前置条件满足的时候,才能被导航到该页面。
可以在路由配置中添加<u>守卫</u>来进行处理。守卫可以返回一个boolean
值,为true
时,导航过程继续,为false
时,导航被取消,当然这时候也可以被导航到其他页面。也可以返回返回一个Observable<boolean>
或Promise<boolean>
,并且路由器会等待这个可观察对象被解析为true
或false
。
路由器支持多种守卫
- 用CanActivate来处理导航到某路由的情况。
- 用CanActivateChild处理导航到子路由的情况。
- 用CanDeactivate来处理从当前路由离开的情况。
- 用Resolve在路由激活之前获取路由数据。
- 用CanLoad来处理异步导航到某特性模块的情况。
使用规则
在分层路由的每个级别上,我们都可以设置多个守卫。 路由器会先按照<u>从最深的子路由由下往上检查的顺序来检查CanDeactivate守护条件。 然后它会按照从上到下的顺序检查CanActivate守卫。 如果任何守卫返回false,其它尚未完成的守卫会被取消,这样整个导航就被取消了。</u>
CanActivate
使用CanActivate
来处理导航路由,需要在路由配置中,添加导入AuthGuard类,修改管理路由并通过CanActivate属性来引用AuthGuard。
具体的守卫规则要看AuthGuard类的实现。而AuthGuard 类是需要继承CanActivate 类的:export class AuthGuard implements CanActivate {}
import { AuthGuard } from '../auth-guard.service';
const adminRoutes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivate: [AuthGuard], // 重点
children: [
{
path: '',
children: [
{ path: 'crises', component: ManageCrisesComponent },
{ path: 'heroes', component: ManageHeroesComponent },
{ path: '', component: AdminDashboardComponent }
],
}
]
}
];
@NgModule({
imports: [
RouterModule.forChild(adminRoutes)
],
exports: [
RouterModule
]
})
export class AdminRoutingModule {}
CanActivateChild 守卫自路由
就像我们可以通过CanActivate来守卫路由一样,我们也能通过CanActivateChild守卫来保护子路由。CanActivateChild守卫的工作方式和CanActivate守卫很相似,不同之处在于它会在每个子路由被激活之前运行。我们保护了管理特性模块不受未授权访问,也同样可以在特性模块中保护子路由。
这个使用起来比较简单,只需要在需要守卫的子路由的配置上添加即可。而AuthGuard 类是需要继承canActivateChild 类的:export class AuthGuard implements canActivateChild {}
const adminRoutes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivate: [AuthGuard],
children: [
{ // 无组件路由,相当于分组
path: '',
canActivateChild: [AuthGuard], // 守卫子路由
children: [
{ path: 'crises', component: ManageCrisesComponent },
{ path: 'heroes', component: ManageHeroesComponent },
{ path: '', component: AdminDashboardComponent }
]
}
]
}
];
CanDeactivate 处理未保存的更改
在现实世界中,我们得先把用户的改动积累起来。 我们可能不得不进行跨字段的校验,可能要找服务器进行校验,可能得把这些改动保存成一种待定状态,直到用户或者把这些改动作为一组进行确认或撤销所有改动。
当用户要导航到外面时,该怎么处理这些既没有审核通过又没有保存过的改动呢? 我们不能马上离开,不在乎丢失这些改动的风险,那显然是一种糟糕的用户体验。
我们应该暂停,并让用户决定该怎么做。如果用户选择了取消,我们就留下来,并允许更多改动。如果用户选择了确认,那就进行保存。 在保存成功之前,我们还可以继续推迟导航。如果我们让用户立即移到下一个界面,而保存却失败了(可能因为数据不符合有效性规则),我们就会丢失该错误的上下文环境。
在等待服务器的答复时,我们没法阻塞它 —— 这在浏览器中是不可能的。 我们只能用异步的方式在等待服务器答复之前先停止导航。 我们需要CanDeactivate守卫。
Resolve
主要实现的就是导航前预先加载路由信息。可以做到,当真正需要导航进来这个详情页面时,是不需要再去获取数据的。是提前加载好的。 服务可以实现Resolve守卫接口来同步或异步解析路由数据。
CanLoad - 保护特性模块的加载
前提
异步路由,只要是懒惰加载特征区域。这样做的好处:
- 可以继续构建特征区,但不再增加初始包大小。
- 只有在用户请求时才加载特征区。
- 为那些只访问应用程序某些区域的用户加快加载速度。
路由器用loadChildren
属性来映射我们希望惰性加载的捆文件,这里是AdminModule。
const appRoutes: Routes = [
{
path: 'admin',
loadChildren: 'app/admin/admin.module#AdminModule',
}
];
- 映射到了我们以前在管理特性区构建的admin.module.ts 文件。
- 在文件路径后面,我们使用# 来标记出文件路径的末尾,并告诉路由器AdminModule 的名字。打开admin.module.ts 文件,我们就会看到它正是我们所导出的模块类的名字。
export class AdminModule {}
简介
我们已经使CanAcitvate
保护AdminModule了,它会阻止对管理特性区的匿名访问。我们在请求时可以异步加载管理类路由,检查用户的访问权,如果用户未登录,则跳转到登陆页面。但更理想的是,我们只在用户已经登录的情况下加载AdminModule,并且直到加载完才放行到它的路由。
我们可以用CanLoad守卫来保证只在用户已经登录并尝试访问管理特性区时才加载一次AdminModule。
几个概念
无组件路由
无组件路由,不借助组件对路由进行分组。来看AdminComponent 下的子路由,我们有一个带path和children的子路由,但它没有使用component。这并不是配置中的失误,而是在使用无组件路由。
代码语言:javascript复制const adminRoutes: Routes = [
{
path: 'admin',
component: AdminComponent,
children: [
{ // 无组件路由
path: '',
children: [
{ path: 'crises', component: ManageCrisesComponent },
{ path: 'heroes', component: ManageHeroesComponent },
{ path: '', component: AdminDashboardComponent }
]
}
]
}
];
@NgModule({
imports: [
RouterModule.forChild(adminRoutes)
],
exports: [
RouterModule
]
})
export class AdminRoutingModule {}
预加载: 在后台加载特征区域
每次导航成功发生时,路由器将查看惰性加载的特征区域的配置,并根据提供的策略作出反应。 路由器默认支持两种预加载策略: 完全不预加载,这是默认值。惰性加载特征区域仍然按需加载。
预加载所有惰性加载的特征区域。
路由器还支持自定义预加载策略,用来精细控制预加载。
自定义预加载策略
- Route Data 启动预加载 其中有参数preload布尔值,如果它为true,就调用内置Router 提供的load函数预主动加载这些特征模块。
- 创建自定义策略 我们将需要实现抽象类PreloadingStrategy和preload方法。在异步加载特征模块和决定是否预加载它们时,路由器调用preload方法。 preload方法有两个参数,第一个参数Route提供路由配置,第二个参数是预加载特征模块的函数。
链接参数数组
链接参数数组保存路由导航时所需的成分:
- 指向目标组件的那个路由的路径(path)
- 必备路由参数和可选路由参数,它们将进入该路由的URL
e.g.我们可以把RouterLink指令绑定到一个数组,就像这样:
代码语言:javascript复制<a [routerLink]="['/heroes']">Heroes</a>
e.g.在指定路由参数时,我们写过一个双元素的数组,就像这样:
代码语言:javascript复制this.router.navigate(['/hero', hero.id]);
e.g.我们可以在对象中提供可选的路由参数,就像这样:
代码语言:javascript复制<a [routerLink]="['/crisis-center', { foo: 'foo' }]">Crisis Center</a>