angular 路由

2023-10-27

参考:angular-路由-文档  路由案例

①.路由参数

paramMap和Snapshot
当组件需要复用的时候使用paramMap获取路由参数:如一个组件不刷新,只更改了路由参数,那么就可以实时获取路由参数
当确定组件不复用的时候直接使用Snapshot获取路由参数,每一次打开这个组件都是一个新的实例

paramMap:

paramMap 的处理过程有点稍复杂。当这个 map 的值变化时,你可以从变化之后的参数中 get() 到其 id 参数。
然后,让 HeroService 去获取一个具有此 id 的英雄,并返回这个 HeroService 请求的结果。
你可能想使用 RxJS 的 map 操作符。 但 HeroService 返回的是一个 Observable<Hero>。 所以你要改用 switchMap 操作符来打平这个 Observable
switchMap 操作符还会取消以前未完成的在途请求。如果用户使用新的 id 再次导航到该路由,而 HeroService 仍在接受老 id 对应的英雄,那么 switchMap 就会抛弃老的请求,并返回这个新 id 的英雄信息。

ngOnInit() {
  this.hero$ = this.route.paramMap.pipe(
    switchMap((params: ParamMap) =>
      this.service.getHero(params.get('id')))
  );
}

成员

说明

has(name)

如果参数名位于参数列表中,就返回 true 。

get(name)

如果这个 map 中有参数名对应的参数值(字符串),就返回它,否则返回 null。如果参数值实际上是一个数组,就返回它的第一个元素。

getAll(name)

如果这个 map 中有参数名对应的值,就返回一个字符串数组,否则返回空数组。当一个参数名可能对应多个值的时候,请使用 getAll

keys

返回这个 map 中的所有参数名组成的字符串数组。

 

Snapshot:
route.snapshot 提供了路由参数的初始值。 你可以通过它来直接访问参数,而不用订阅或者添加 Observable 的操作符。 这样在读写时就会更简单:

ngOnInit() {
  let id = this.route.snapshot.paramMap.get('id');

  this.hero$ = this.service.getHero(id);
}

②.第二路由:

在app-routing.module.ts中添加如下代码

{
  path: 'compose',
  component: ComposeMessageComponent,
  outlet: 'popup'
},

在app.component.html中添加如下代码:

<a [routerLink]="[{ outlets: { popup: ['compose'] } }]">Contact</a><br/>

<router-outlet name="popup"></router-outlet>

运行后,在页面上点击Contact按钮,会出现ComposeMessageComponent的内容.
这时候浏览器地址栏后面会出现http://localhost:4200/xx/xx(popup:compose),如果此时切换主路由,第二路由是不会发生变化的
路由器在导航树中对两个独立的分支保持追踪,并在 URL 中对这棵树进行表达。

清除第二路由

this.router.navigate([{ outlets: { popup: null }}]);

③.路由守卫

1.CanActivate: 要求认证
应用程序通常会根据访问者来决定是否授予某个特性区的访问权。 你可以只对已认证过的用户或具有特定角色的用户授予访问权,还可以阻止或限制用户访问权,直到用户账户激活为止。
CanActivate 守卫是一个管理这些导航类业务规则的工具。
使用命令在app/auth文件夹下创建一个守卫文件  ng generate guard auth/auth

src/app/auth/auth.guard.ts中canActivate()方法解读:

 canActivate(  next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    let url: string = state.url;

    return this.checkLogin(url);  // 自己定义的方法.
  }

checkLogin(url: string): boolean {
    if (this.authService.isLoggedIn) { return true; }

    this.authService.redirectUrl = url;
    this.router.navigate(['/login']);
    return false;
  }

// this.checkLogin(url)返回的结果只能是true或者false,
// 当checkLogin(url)返回为true的时候就表示守卫通过,可以访问被守卫的路由
// 当checkLogin(url)返回为false的时候就表示守卫通不过,不可以访问被守卫的路由

被守卫的路由需要使用 canActivate: [AuthGuard], 
如果canActivate()返回结果为true,则可以访问admin下面的子路由,否组不可以

import { AuthGuard } from '../auth/auth.guard';

const adminRoutes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [AuthGuard],
    children: [
       ...
    ]
  }
];

...

2.CanActivateChild:保护子路由 
你还可以使用 CanActivateChild 守卫来保护子路由。 CanActivateChild 守卫和 CanActivate 守卫很像。 它们的区别在于,CanActivateChild 会在任何子路由被激活之前运行。

src/app/auth/auth.guard.ts中添加canActivateChild()方法

canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    return this.canActivate(route, state);
  }

把这个 AuthGuard 添加到“无组件的”管理路由,来同时保护它的所有子路由,而不是为每个路由单独添加这个 AuthGuard

const adminRoutes: Routes = [
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [AuthGuard],
    children: [
      {
        path: '',
        canActivateChild: [AuthGuard], // CanActivateChild 会在任何子路由被激活之前运行
        children: [
          { path: 'crises', component: ManageCrisesComponent },
          { path: 'heroes', component: ManageHeroesComponent },
          { path: '', component: AdminDashboardComponent }
        ]
      }
    ]
  }
];

@NgModule({
  imports: [
    RouterModule.forChild(adminRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class AdminRoutingModule {}

3.CanDeactivate:处理未保存的更改(离开路由时,如果有未保存的信息)
命令行新建一个guard 文件 ng generate guard can-deactivate;

src/app/can-deactivate.guard.ts代码如下:

import { Injectable }    from '@angular/core';
import { CanDeactivate } from '@angular/router';
import { Observable }    from 'rxjs';

export interface CanComponentDeactivate {
 canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}

@Injectable({
  providedIn: 'root',
})
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
  canDeactivate(component: CanComponentDeactivate) {
    return component.canDeactivate ? component.canDeactivate() : true;
  }
}

上面的代码中:
1.component.canDeactivate:canDeactivate()为使用的那个组件中的方法 ,返回的结果也只能是true或者false
2.如果有canDeactivate()方法,就使用canDeactivate.如果没有就返回true,true则是正常离开路由,否则运行对应的程序

路由中使用:

children: [
  {
    path: ':id',
    component: CrisisDetailComponent,
    canDeactivate: [CanDeactivateGuard]
  }          
]

component中使用:

 canDeactivate(): Observable<boolean> | boolean {
    if (!this.crisis || this.crisis.name === this.editName) {
      return true;
    }
    return this.dialogService.confirm('Discard changes?');
  }

component的代码中:
1.canDeactivate为自己定义的方法,can-deactivate.guard.ts中会用到component.canDeactivate就是此方法
那么离开路由的时候,如何知道当前页面中哪些数据变化了,这时候就要用到Resolve

4.Resolve: 预先获取组件数据    参考:Resolve
新建service 文件 ng generate service crisis-center/crisis-detail-resolver

crisis-detail-resolver.ts代码如下:

...
import { CrisisService }  from './crisis.service';
import { Crisis } from './crisis';

@Injectable({
  providedIn: 'root',
})
export class CrisisDetailResolverService implements Resolve<Crisis> {
  constructor(private cs: CrisisService, private router: Router) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Crisis> | Observable<never> {
    let id = route.paramMap.get('id');
    //this.cs.getCrisis(id):自定义的方法,根据id获取当前数据.返回的数据在component中可以通过ActivatedRoute.data获取
    return this.cs.getCrisis(id).pipe(
      take(1), // 只取一次数据
      mergeMap(crisis => { // 同时合并多个流
        if (crisis) {
          return of(crisis);
        } else { // id 没找到的话,直接跳转到/crisis-center
          this.router.navigate(['/crisis-center']);
          return EMPTY;
        }
      })
    );
  }
}

路由中使用:

import { CrisisDetailResolverService } from './crisis-detail-resolver.service';

...

children: [
  {
    path: ':id',
    component: CrisisDetailComponent,
    canDeactivate: [CanDeactivateGuard],
    resolve: {
      crisis: CrisisDetailResolverService
    }
  },
  ...
]

component中使用:

 constructor( private route: ActivatedRoute, private router: Router, public dialogService: DialogService ) {}

  ngOnInit() {
    this.route.data
      .subscribe((data: { crisis: Crisis }) => {
        this.editName = data.crisis.name;
        this.crisis = data.crisis;
      });
  }

// this.route.data中就是crisis-detail-resolver.ts中resolve()的返回值

5.查询参数及片段     参考:查询参数及片段
如何定义一些所有路由中都可用的可选参数呢? 这就该“查询参数”登场了。

src/app/auth/auth.guard.ts中设置

 checkLogin(url: string): boolean {
    if (this.authService.isLoggedIn) { return true; }
    this.authService.redirectUrl = url;
    let sessionId = 123456789;

    // 设置
    let navigationExtras: NavigationExtras = {
      queryParams: { 'session_id': sessionId },
      fragment: 'anchor'
    };
    this.router.navigate(['/login'], navigationExtras);
    return false;
  }

component中使用:

this.route
.queryParamMap
.pipe(map(params => params.get('session_id') || 'None'));

④:异步路由

在继续构建特征区的过程中,应用的尺寸将会变得更大。在某一个时间点,将达到一个顶点,应用将会需要过多的时间来加载。如何才能解决这个问题呢?通过引进异步路由,可以获得在请求时才惰性加载特性模块的能力。 惰性加载有多个优点:
· 你可以只在用户请求时才加载某些特性区。
· 对于那些只访问应用程序某些区域的用户,这样能加快加载速度。
· 你可以持续扩充惰性加载特性区的功能,而不用增加初始加载的包体积。

1.惰性加载路由配置
Router 支持空路径路由,可以使用它们来分组路由,而不用往 URL 中添加额外的路径片段
使用 loadChildren 属性替换掉 children 属性。 loadChildren 属性接收一个函数,该函数使用浏览器内置的动态导入语法 import('...') 来惰性加载代码,并返回一个承诺(Promise)

 admin-routing.module.ts是一个子路由,正常如下,并且在app.module.ts中imports: [ AdminModule,AppRoutingModule],

...
const adminRoutes: Routes = [{
    path: 'admin',
    component: AdminComponent,
    children: [{ path: 'crises', component: ManageCrisesComponent },...]
  }];
@NgModule({
  imports: [RouterModule.forChild(adminRoutes)],
  exports: [RouterModule]
})
export class AdminRoutingModule { }

 app.routing-module.ts如下

const appRoutes: Routes = [
  { path: '',   redirectTo: '/heros', pathMatch: 'full' }, // 定向重路由
  { path: '**', component: PageNotFoundComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(appRoutes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

 改造
1.把 admin-routing.module.ts 中的 admin 路径从 'admin' 改为空路径 ''
2.app-routing.module.ts中的appRoutes下添加如下代码

{
  path: 'admin',
  loadChildren: () => import('./admin/admin.module').then(mod => mod.AdminModule),
},

3.预加载的话,要在在app.module.ts中@NgModule=>imports数组中注释掉 AdminModule,

2.CanLoad守卫:保护对特性模块的未授权加载
在使用CanActivate的情况下,AdminModule会阻止未授权用户跳转到AdminModule模块,虽然阻止了跳转但是路由仍然会加载AdminModule(即使用户无法访问它的任何一个组件),理想的方式是,只有在用户已登录的情况下你才加载 AdminModule

将路由添加一个CanLoad守卫,它只在用户已登录并且尝试访问管理特性区的时候,才加载 AdminModule 一次。

src/app/auth/auth.guard.ts添加如下代码,this.checkLogin(url)方法在③-1和③-5中

canLoad(route: Route): boolean {
  let url = `/${route.path}`;

  return this.checkLogin(url);
} 

app-routing.module.ts (lazy admin route)

{
  path: 'admin',
  loadChildren: () => import('./admin/admin.module').then(mod => mod.AdminModule),
  canLoad: [AuthGuard]
},

⑤预加载:特性区的后台加载 - 异步加载模块

AppModule 在应用启动时就被加载了,它是立即加载的。 而 AdminModule(loadChildren)子路由 只有当用户点击某个链接时才会加载,它是惰性加载的。预加载是介于两者之间的一种方式
为了获得尽可能小的初始加载体积和最快的加载速度,应该对 第一视图(AppModule 和 默认显示的路由) 进行急性加载

1.什么是这就是预加载?
比如现在有CrisisCenterModule和HerosModule模块,用户默认显示HerosModule模块 ,几乎可以肯定用户会在启动应用之后的几分钟内访问CrisisCenterModule,理想情况下,应用启动时应该只加载 AppModule 和 HeroesModule,然后几乎立即开始后台加载 CrisisCenterModule。 在用户浏览到CrisisCenterModule之前,该模块应该已经加载完毕,可供访问了。

2.预加载的工作原理
在每次成功的导航后,路由器会在自己的配置中查找尚未加载并且可以预加载的模块。 是否加载某个模块,以及要加载哪些模块,取决于预加载策略。
3.Router内置了两种预加载策略:
3.1.完全不预加载,这是默认值。惰性加载的特性区仍然会按需加载。
3.2.预加载所有惰性加载的特性区。
4.惰性加载AdminModule步骤,同:④ - 1 (惰性加载路由配置)
    把 AdminModule 中的路径从 'adimin' 改为空字符串。 同④ 1.1
    往 AppRoutingModule 中添加一个 admin 路由。 同④ 1.2
    设置 loadChildren 字符串来加载 AdminModule。 同④ 1.2
    从 app.module.ts 中移除所有对 AdminModule 的引用。 同④ 1.3

要为所有惰性加载模块启用预加载功能,请从 Angular 的路由模块中导入 PreloadAllModules
src/app/app-routing.module.ts 

RouterModule.forRoot(
  appRoutes,
  { preloadingStrategy: PreloadAllModules}
)

5.自定义预加载策略

命令行新建文件ng generate service selective-preloading-strategy
selective-preloading-strategy.ts代码如下:

import { Injectable } from '@angular/core';
import { PreloadingStrategy, Route } from '@angular/router';
import { Observable, of } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class SelectivePreloadingStrategyService implements PreloadingStrategy {
  preloadedModules: string[] = [];

  preload(route: Route, load: () => Observable<any>): Observable<any> {
    if (route.data && route.data['preload']) {
     
      this.preloadedModules.push(route.path);
      console.log('Preloaded: ' + route.path);

      return load();
    } else {
      return of(null);
    }
  }
}

路由更改添加data: { preload: true }:

{
  path: 'crisis-center',
  loadChildren: () => import('./crisis-center/crisis-center.module').then(mod => mod.CrisisCenterModule),
  data: { preload: true }
},

component.ts中获取:

constructor(preloadStrategy: SelectivePreloadingStrategyService) {
    this.modules = preloadStrategy.preloadedModules; // modules:string[]
  }

最后在路由中preloadingStrategy

@NgModule({
imports: [
    RouterModule.forRoot(
      appRoutes,
      {preloadingStrategy: SelectivePreloadingStrategyService,}
    )
],
...
})

 这时候访问heros页面的时候,CrisisCenterModule 也被预加载了,可以在控制台看下输出

⑥使用重定向迁移 URL

// 原始路由
const heroesRoutes: Routes = [
  { path: 'heroes',  component: HeroListComponent, data: { animation: 'heroes' } },
  { path: 'hero/:id', component: HeroDetailComponent, data: { animation: 'hero' } }
];

// 使用重定向迁移 URL
const heroesRoutes: Routes = [
  { path: 'heroes', redirectTo: '/superheroes' },
  { path: 'hero/:id', redirectTo: '/superhero/:id' },
  { path: 'superheroes',  component: HeroListComponent, data: { animation: 'heroes' } },
  { path: 'superhero/:id', component: HeroDetailComponent, data: { animation: 'hero' } }
];

总结:

添加路由出口:<router-outlet></router-outlet>
定义通配符路由:{ path: '**', component: PageNotFoundComponent }
默认路由redirect:  { path: '', redirectTo: '/heroes', pathMatch: 'full' },
paramMap和Snapshot:获取路由参数,paramMap实时获取,Snapshot获取一次
CanActivate:路由守卫
CanActivateChild:保护子路由,会在任何子路由被激活之前运行
CanDeactivate:处理未保存的更改
Resolve: 预先获取组件数据
loadChildren:惰性加载路由配置
CanLoad守卫:保护对特性模块的未授权加载

定义路由,注册路由:

// 定义路由
const appRoutes: Routes = [
  { path: 'crisis-center', component: CrisisListComponent },
  { path: 'heroes', component: HeroListComponent },
];

// 注册路由:
@NgModule({
  imports: [
    ...
    RouterModule.forRoot(appRoutes)
  ],
  ...
})

父路由,子路由

// 子路由AdminRoutingModule
...

const adminRoutes: Routes = [
  {
    path: '',
    component: AdminComponent,
    children: [
       { path: 'crises', component: ManageCrisesComponent },
       { path: 'heroes', component: ManageHeroesComponent },
       { path: '', component: AdminDashboardComponent }
    ]
  }
];

@NgModule({
  imports: [RouterModule.forChild(adminRoutes)],
  exports: [RouterModule]
})
export class AdminRoutingModule { }

// 父路由,AppRoutingModule
...
const appRoutes: Routes = [
  { path: 'crisis-center', component: CrisisListComponent },
  { path: 'heroes', component: HeroListComponent },
];

@NgModule({
  imports: [
    ...
    RouterModule.forRoot(appRoutes)
  ],
  ...
})

//app.module.ts
@NgModule({
  imports: [AdminModule,AppRoutingModule,...],
  ...
})

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

angular 路由 的相关文章

随机推荐

  • Failed to load module “canberra-gtk-module“ 或 Using GTK+ 2.x and GTK+ 3 in the same process is not

    项目场景 ubuntu安装matlab之后 运行时报错Failed to load module canberra gtk module 如下 原因分析 未安装Matlab运行所需要的依赖 执行如下命令 sudo apt get insta
  • 深入浅出UML类图(二)

    类与类之间的关系 1 在软件系统中 类并不是孤立存在的 类与类之间存在各种关系 对于不同类型的关系 UML提供了不同的表示方式 1 关联关系 关联 Association 关系是类与类之间最常用的一种关系 它是一种结构化关系 用于表示一类对
  • 树的四种遍历C/C++实现

    树的四种遍历C C 实现 树的四种遍历C C 实现 结构定义 先序创建树 先序遍历 中序遍历 后序遍历 层次遍历 总代码 给懒人下载运行用 运行示例 树的四种遍历C C 实现 备考期末 懂得都懂 手敲遍代码 比较懒 都是递归形式 结构定义
  • error LNK2019: 无法解析的外部符号 WSAStartup,该符号在函数 “public:

    error LNK2019 无法解析的外部符号 gethostname 该符号在函数 public static bool cdecl gdcm System GetHostName char const GetHostName Syste
  • Fragment 实现简易新闻界面(适配手机与Pad)

    一 前言 Android 在 Android 3 0 API 级别 11 中引入了 Fragment 主要目的是为大屏幕 如平板电脑 上更加动态和灵活的界面设计提供支持 由于平板电脑的屏幕尺寸远胜于手机屏幕尺寸 因而有更多空间可供组合和交换
  • 前端白屏检测方案

    早期因为浏览器 技术 兼容性等诸多问题 导致网页的显示效果非常的单一 基本都是静态页 后续随着Angular React Vue等前端框架的出现 采用SPA单页面应用的方案越来越多 用户和企业对于页面的稳定性 性能有了更高的诉求 根据Abe
  • 为什么我们的自动化测试“要”这么难

    为什么我们的自动化测试 要 这么难 笔者在别的贴子里面曾提过 自己所在部门的 自动化测试经历了几次步进式的建设 都具有阶段性的成果 但是总的看来却不是一个成功的案例 因为赶进度 仓促的投入让一大堆的脚本质量比较低下 有几个测试组由于没有人力
  • Selenium

    Selenium 参考崔庆才爬虫 安装 pip install selenium 注意 需要下载Chrome的 webdriverwebdriver 下载完成后解压到与python exe可执行文件同一目录下 基本使用 from selen
  • this.getClass().getClassLoader().getResourceAsStream找不到文件

    this getClass getClassLoader getResourceAsStream 路径正确 但是找不到文件时 要检查对应xml文件是否放到了这个地方 只有放到了才能找到
  • 安卓手机GPU OpenCL总结

    前段时间 把市面上手机GPU OpenCL支持情况做了一个总结 总结如下 目前 手机 GPU 市面有四个公司产品 Qualcomm Imagination Technologies ARM Vivante 分别对应的产品如下 所有表格均是按
  • Springboot课程试题库管理系统毕业设计源码271129

    Springboot 计算机网络原理 课程试题库管理系统 摘 要 信息化社会内需要与之针对性的信息获取途径 但是途径的扩展基本上为人们所努力的方向 由于站在的角度存在偏差 人们经常能够获得不同类型信息 这也是技术最为难以攻克的课题 针对 计
  • 录制、回放乱码问题解决办法和快照问题解决办法.

    1 录制的脚本乱码 LR录制的脚本中可能会有乱码 主要是当URL中有中文时 通过如下问题可以解决此问题 a Go to Vugen gt Tools gt Recording Options gt Advanced b Check the
  • 失业的程序员(六):加班

    本系列前章 失业的程序员 一 二 三 四 五 一 本文前戏 谈爱 每次开文我总要说一些看起来和本文其实关系不大的啰嗦话 也希望各位观众能够习惯 稍微花费大家几分钟时间便可进入正文 再一次跪求谅解 前几天在家看 我是歌手 复活赛那期 着实震撼
  • 1.开始学习前端(HTML+CSS+JavaScript)学习记录

    1 了解前端 Web开发 对于网页开发 最基础的 最核心的技术就是html css javascript 简称js 这三个技术也被称为前端开发 新三剑客 在Web1 0时代的 网页制作 网页三剑客是指网站的开发工具 Dreamweaver
  • 2009年8月21日

    开通博客了 new Start 1 加了的Active控件Windows Media Player如何能使用快进FastForward 和快退FastForward 这两个功能呢 给控件关联一个control变量 然后调用FastForwa
  • 大数据分析陷阱与Simpson’s Paradox(辛普森悖论)

    在大数据分析时 你有没有遇到这样一种奇怪现象 当分开看数据的时候会得到一种结论 但是合起来之后发现情况却完全改变 这就是著名的辛普森悖论 它总是隐藏在大数据之中 成为大数据分析的陷阱之一 1 含义 辛普森悖论 Simpson s Parad
  • 用C语言进行面向对象编程

    在C语言中进行面向对象编程需要使用一些特定的技术和方法 具体如下 结构体 在C语言中 结构体可以用来表示一个对象的属性和状态 相当于一个类的实例变量 结构体中可以包含不同类型的数据成员 如整数 字符 指针等 函数指针 C语言中可以使用函数指
  • 全局配置Element UI 中的 $message 的显示时长

    首先说下我是全局引入的Element UI组件 这是要更改message 的默认的时长 重写message的方法 import ElementUI from element ui import element ui lib theme ch
  • 修改 el-dialog__body padding

    在dialog外部套个div 再写deep addDia deep el dialog body padding bottom 0
  • angular 路由

    参考 angular 路由 文档 路由案例 路由参数 paramMap和Snapshot 当组件需要复用的时候使用paramMap获取路由参数 如一个组件不刷新 只更改了路由参数 那么就可以实时获取路由参数 当确定组件不复用的时候直接使用S