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 相关文章推荐
在textarea文本域中显示HTML代码的方法
Mar 06 Javascript
jQuery获取动态生成的元素示例
Jun 15 Javascript
Javascript+CSS实现影像卷帘效果思路及代码
Oct 20 Javascript
jquery表单插件Autotab使用方法详解
Jun 24 Javascript
微信小程序 开发MAP(地图)实例详解
Jun 27 Javascript
jQueryMobile之窗体长内容的缺陷与解决方法实例分析
Sep 20 jQuery
vue打包后显示空白正确处理方法
Nov 01 Javascript
javascript Function函数理解与实战
Dec 01 Javascript
layui从数据库中获取复选框的值并默认选中方法
Aug 15 Javascript
node省市区三级数据性能测评实例分析
Nov 06 Javascript
vue中在vuex的actions中请求数据实例
Nov 08 Javascript
Quasar Input:type=&quot;number&quot; 去掉上下小箭头 实现加减按钮样式功能
Apr 09 Javascript
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脚本中include文件出错解决方法
2008/11/20 PHP
CodeIgniter框架URL路由总结
2014/09/03 PHP
php+ajax登录跳转登录实现思路
2016/07/31 PHP
php PDO属性设置与操作方法分析
2018/12/27 PHP
php写入mysql中文乱码的实例解决方法
2019/09/17 PHP
Laravel5.1 框架表单验证操作实例详解
2020/01/07 PHP
jQuery右键菜单contextMenu使用实例
2011/09/28 Javascript
jQuery操作 input type=checkbox的实现代码
2012/06/14 Javascript
Js+Flash实现访问剪切板操作
2012/11/20 Javascript
document.all的一个比较完整的总结及案例
2013/01/31 Javascript
Javascript如何判断数据类型和数组类型
2016/06/22 Javascript
微信小程序 地图(map)实例详解
2016/11/16 Javascript
几行js代码实现自适应
2017/02/24 Javascript
详解Vue组件之间的数据通信实例
2017/06/17 Javascript
vue axios 给生产环境和发布环境配置不同的接口地址(推荐)
2018/05/08 Javascript
JS中call和apply函数用法实例分析
2018/06/20 Javascript
vue做移动端适配最佳解决方案(亲测有效)
2018/09/04 Javascript
vue生命周期和react生命周期对比【推荐】
2018/09/19 Javascript
JS实现随机生成10个手机号的方法示例
2018/12/07 Javascript
基于vue如何发布一个npm包的方法步骤
2019/05/15 Javascript
浅谈react-router@4.0 使用方法和源码分析
2019/06/04 Javascript
[51:22]Fnatic vs IG 2018国际邀请赛小组赛BO2 第一场 8.17
2018/08/18 DOTA
浅谈Python程序与C++程序的联合使用
2015/04/07 Python
如何基于Python实现自动扫雷
2020/01/06 Python
CSS3绘制不规则图形的一些方法示例
2015/11/07 HTML / CSS
德国BA保镖药房韩文网:kr.ba.de
2017/09/04 全球购物
美国婴童服装市场上的领先品牌:Carter’s
2018/02/08 全球购物
Python使用openpyxl复制整张sheet
2021/03/24 Python
公司道歉信范文
2014/01/09 职场文书
计算机个人求职信范例
2014/01/24 职场文书
党的群众教育实践活动实施方案
2014/06/12 职场文书
三八妇女节超市活动方案
2014/08/18 职场文书
大学生党员个人对照检查材料范文
2014/09/25 职场文书
行政文员岗位职责
2015/02/04 职场文书
开除员工通知
2015/04/22 职场文书
小学四年级作文之写景
2019/08/23 职场文书