Angular2 之 路由与导航详细介绍


Posted in Javascript onMay 26, 2017

导航是很简单的,只是不同页面之间的切换,路由是实现导航的一种。

一个url对应的一个页面,在angular2中是一个组件。定义一个规则。

基础知识

大多数带路由的应用都要在index.html的标签下先添加一个元素,来告诉路由器该如何合成导航用的URL。

路由是从@angular/router包中引入的。

路由都是需要进行配置的。而这个配置需要的也就是RouterModule模块。

一个路由配置

path中不能用斜线/开头。

这些路由的定义顺序是故意如此设计的。 路由器使用先匹配者优先的策略来匹配路由,所以,具体路由应该放在通用路由的前面。在上面的配置中,带静态路径的路由被放在了前面,后面是空路径路由,因此它会作为默认路由。而通配符路由被放在最后面,这是因为它是最通用的路由,应该只在前面找不到其它能匹配的路由时才匹配它。

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:

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单独的导航,这也就是组件路由。

平级的路由

@NgModule({
 imports: [
 RouterModule.forChild([
  { path: 'heroes', component: HeroListComponent },
  { path: 'hero/:id', component: HeroDetailComponent },
  { path:'heroform', component: HeroFormComponent },
 ])
 ],
 exports: [
 RouterModule
 ]
})
export class HeroRoutingModule { }

我们还有另外一中类型的路由组织方式,路由树的形式。

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'
},

路由守卫

简介

路由守卫,应用在这个路由不是对所有导航都有效的,是有一些前置条件的,只有当这些前置条件满足的时候,才能被导航到该页面。

可以在路由配置中添加守卫来进行处理。守卫可以返回一个boolean值,为true时,导航过程继续,为false时,导航被取消,当然这时候也可以被导航到其他页面。也可以返回返回一个Observable<boolean>或Promise<boolean>,并且路由器会等待这个可观察对象被解析为true或false。

路由器支持多种守卫

用CanActivate来处理导航到某路由的情况。

用CanActivateChild处理导航到子路由的情况。

用CanDeactivate来处理从当前路由离开的情况。

用Resolve在路由激活之前获取路由数据。

用CanLoad来处理异步导航到某特性模块的情况。

使用规则

在分层路由的每个级别上,我们都可以设置多个守卫。 路由器会先按照从最深的子路由由下往上检查的顺序来检查CanDeactivate守护条件。 然后它会按照从上到下的顺序检查CanActivate守卫。 如果任何守卫返回false,其它尚未完成的守卫会被取消,这样整个导航就被取消了。

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 - 保护特性模块的加载

前提

异步路由,只要是懒惰加载特征区域。这样做的好处:

  1. 可以继续构建特征区,但不再增加初始包大小。
  2. 只有在用户请求时才加载特征区。
  3. 为那些只访问应用程序某些区域的用户加快加载速度。

路由器用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。这并不是配置中的失误,而是在使用无组件路由。

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 {}

预加载: 在后台加载特征区域

每次导航成功发生时,路由器将查看惰性加载的特征区域的配置,并根据提供的策略作出反应。 路由器默认支持两种预加载策略:

  1. 完全不预加载,这是默认值。惰性加载特征区域仍然按需加载。
  2. 预加载所有惰性加载的特征区域。

路由器还支持自定义预加载策略,用来精细控制预加载。

自定义预加载策略

Route Data 启动预加载

其中有参数preload布尔值,如果它为true,就调用内置Router

提供的load函数预主动加载这些特征模块。

创建自定义策略

我们将需要实现抽象类PreloadingStrategy和preload方法。在异步加载特征模块和决定是否预加载它们时,路由器调用preload方法。 preload方法有两个参数,第一个参数Route提供路由配置,第二个参数是预加载特征模块的函数。

链接参数数组

链接参数数组保存路由导航时所需的成分:

  1. 指向目标组件的那个路由的路径(path)
  2. 必备路由参数和可选路由参数,它们将进入该路由的URL

e.g.我们可以把RouterLink指令绑定到一个数组,就像这样:

<a [routerLink]="['/heroes']">Heroes</a>

e.g.在指定路由参数时,我们写过一个双元素的数组,就像这样:

this.router.navigate(['/hero', hero.id]);

e.g.我们可以在对象中提供可选的路由参数,就像这样:

<a [routerLink]="['/crisis-center', { foo: 'foo' }]">Crisis Center</a>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js left,right,mid函数
Jun 10 Javascript
浅析jQuery中调用ajax方法时在不同浏览器中遇到的问题
Jun 11 Javascript
JavaScript操作Cookie方法实例分析
May 27 Javascript
13个PHP函数超实用
Oct 21 Javascript
Bootstrap选项卡与Masonry插件的完美结合
Jul 06 Javascript
Bootstrap源码学习笔记之bootstrap进度条
Dec 24 Javascript
JavaScript调用模式与this关键字绑定的关系
Apr 21 Javascript
深入理解Vue 的钩子函数
Sep 05 Javascript
JS 音频可视化插件Wavesurfer.js的使用教程
Oct 31 Javascript
JS如何寻找数组中心索引过程解析
Jun 01 Javascript
vue项目中js-cookie的使用存储token操作
Nov 13 Javascript
vue首次渲染全过程
Apr 21 Vue.js
Agularjs妙用双向数据绑定实现手风琴效果
May 26 #Javascript
详解vue-cli快速构建项目以及引入bootstrap、jq
May 26 #Javascript
基于layer.js实现收货地址弹框选择然后返回相应的地址信息
May 26 #Javascript
jQuery导航条固定定位效果实例代码
May 26 #jQuery
mongoose中利用populate处理嵌套的方法
May 26 #Javascript
Angularjs修改密码的实例代码
May 26 #Javascript
详解vue.js的devtools安装
May 26 #Javascript
You might like
php xml文件操作实现代码(二)
2009/03/20 PHP
PHP命令行脚本接收传入参数的三种方式
2014/08/20 PHP
深入探究PHP的多进程编程方法
2015/08/18 PHP
PHP的Yii框架中行为的定义与绑定方法讲解
2016/03/18 PHP
Laravel最佳分割路由文件(routes.php)的方式
2016/08/04 PHP
源码分析 Laravel 重复执行同一个队列任务的原因
2017/12/25 PHP
PHP多线程模拟实现秒杀抢单
2018/02/07 PHP
PHP共享内存使用与信号控制实例分析
2018/05/09 PHP
浅谈php://filter的妙用
2019/03/05 PHP
PHP中SESSION过期设置
2021/03/09 PHP
JavaScript Cookie的读取和写入函数
2009/12/08 Javascript
javaScript arguments 对象使用介绍
2013/10/18 Javascript
详解Jquery实现ready和bind事件
2016/04/14 Javascript
原生js实现查询天气小应用
2016/12/09 Javascript
javascript实现下雨效果
2017/03/27 Javascript
Vue-Router进阶之滚动行为详解
2017/09/13 Javascript
JavaScript寄生组合式继承实例详解
2018/01/06 Javascript
Vue 项目中遇到的跨域问题及解决方法(后台php)
2018/03/28 Javascript
JavaScript学习笔记之基于定时器实现图片无缝滚动功能详解
2019/01/09 Javascript
解决vue+router路由跳转不起作用的一项原因
2020/07/19 Javascript
[01:17:47]TNC vs VGJ.S 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
python使用reportlab实现图片转换成pdf的方法
2015/05/22 Python
Python视频爬虫实现下载头条视频功能示例
2018/05/07 Python
详解Python3序列赋值、序列解包
2019/05/14 Python
python使用正则表达式(Regular Expression)方法超详细
2019/12/30 Python
美国在线家居装饰店:Belle&June
2018/10/24 全球购物
优秀学生评语大全
2014/04/25 职场文书
生产工厂门卫岗位职责
2014/09/26 职场文书
2014年关工委工作总结
2014/11/17 职场文书
优秀党员先进材料
2014/12/18 职场文书
入党转正申请书范文
2019/05/20 职场文书
JavaScript使用canvas绘制坐标和线
2021/04/28 Javascript
Jupyter Notebook 如何修改字体和大小以及更改字体样式
2021/06/03 Python
Oracle 死锁的检测查询及处理
2021/09/25 Oracle
梳理总结Python开发中需要摒弃的18个坏习惯
2022/01/22 Python
关于Redis的主从复制及哨兵问题
2022/06/16 Redis