zoukankan      html  css  js  c++  java
  • Angular6 学习笔记——路由详解

    angular6.x系列的学习笔记记录,仍在不断完善中,学习地址:

    https://www.angular.cn/guide/template-syntax

    http://www.ngfans.net/topic/12/post/2

    系列目录

    (1)组件详解之模板语法

    (2)组件详解之组件通讯

    (3)内容投影, ViewChild和ContentChild

    (4)指令

    (5)路由

     路由存在的意义

    一般而言,浏览器具有下列导航模式:

        在地址栏输入 URL,浏览器就会导航到相应的页面。

        在页面中点击链接,浏览器就会导航到一个新页面。

        点击浏览器的前进和后退按钮,浏览器就会在你的浏览历史中向前或向后导航。

    那么,在angular中,是什么决定上述的行为呢?

    对于一个新建的项目而言,只存在一个组件AppComponent,如果不增加其他的组件,意味着所有的行为就将在这一个组件里面完成,这种情况下,单一的组件将无法保存状态的变化,这显然满足不了上面的需求.所以,通常情况下,会如我在组件通讯中所写,组件之间呈如下的树形结构:

    路由就是连接这些组件的筋络,它也是树形结构的.有了它,就可以在angular中实现上述的导航模式

    可以把路由看成是一组规则,它决定了url的变化对应着哪一种状态,具体表现就是不同视图的切换

    在angular中,路由是非常重要的组成部分, 组件的实例化与销毁,模块的加载,组件的某些生命周期钩子的发起,都是与它有关

    路由定义

    我们从新建一个项目来演示一下路由的基本定义,对新建项目加以丰富,增添娱乐,学习,工作三个组件,对应的文件结构如下:

    │  app.component.html
    │  app.component.ts
    │  app.module.ts
    │  
    └─routes
        ├─happy
        │      happy.component.html
        │      happy.component.ts
        │      
        ├─study
        │      study.component.html
        │      study.component.ts
        │      
        └─work
                work.component.html
                work.component.ts
    文件结构

    如何能够实现以下的效果呢?

    当前Url 对应组件内容
    localhost:XXX/ HappyComponent
    localhost:XXX/work WorkComponent
    localhost:XXX/happy HappyComponent
    localhost:XXX/study StudyComponent
    localhost:XXX/nothing HappyComponent

      

    在AppModule中增加如下的路由配置,即可满足上面的需求

     1 import { BrowserModule } from '@angular/platform-browser';
     2 import { NgModule } from '@angular/core';
     3 import { AppComponent } from './app.component';
     4 import { WorkComponent } from './routes/work/work.component';
     5 import { StudyComponent } from './routes/study/study.component';
     6 import { HappyComponent } from './routes/happy/happy.component';
     7 import { Routes, RouterModule } from '@angular/router';
     8 
     9 const appRoutes: Routes = [
    10   { path: '', component: HappyComponent },
    11   { path: 'work', component: WorkComponent },
    12   { path: 'happy', component: HappyComponent },
    13   { path: 'study', component: StudyComponent },
    14   { path: '**', component: HappyComponent },
    15 ]
    16 
    17 @NgModule({
    18   declarations: [
    19     AppComponent,
    20     WorkComponent,
    21     StudyComponent,
    22     HappyComponent
    23   ],
    24   imports: [
    25     RouterModule.forRoot(appRoutes),
    26     BrowserModule,
    27   ],
    28   providers: [],
    29   bootstrap: [AppComponent]
    30 })
    31 
    32 export class AppModule { }

     也许上面有一些陌生的东西,但这并不重要,让我们在介绍路由模块之后一起来介绍

    路由模块

    在上述路由定义中,是把路由的配置放在模块AppModule中进行的,这样在简单的配置中,是可以接受的.

    但是随着应用的成长,会用到更多路由器特性,比如:守卫,解析器和子路由等,这时候再在模块中进行配置,就会显得杂乱冗肿

    官方建议重构路由,将路由信息移到一个单独的特殊用途模块之中,叫做路由模块。

    路由模块并不是必须的,它只是优化设计的一种选择,它的价值在配置很复杂,并包含专门守卫和解析器服务时尤其明显。能够保持设计的一致性和代码的干净,便于开发者查找和扩展配置.当然,在配置很简单时,它可能看起来很多余。

    路由模块有一系列特性:

    • 把路由这个关注点从其它应用类关注点中分离出去。

    • 测试特性模块时,可以替换或移除路由模块。

    • 为路由服务提供商(包括守卫和解析器等)提供一个共同的地方。

    • 不要声明组件。

    将上面的模块中的路由定义转换成路由模块,文件结构变化如下:

    │  app-routing.module.ts
    │  app.component.html
    │  app.component.ts
    │  app.module.ts
    │  
    └─routes
        │  
        ├─happy
        │      happy.component.html
        │      happy.component.ts
        │      
        ├─study
        │      study.component.html
        │      study.component.ts
        │      
        └─work
                work.component.html
                work.component.ts
    文件结构

    具体代码:

     1 import { BrowserModule } from '@angular/platform-browser';
     2 import { NgModule } from '@angular/core';
     3 import { AppComponent } from './app.component';
     4 import { AppRoutingModule } from './app-routing.module';
     5 import { WorkComponent } from './routes/work/work.component';
     6 import { StudyComponent } from './routes/study/study.component';
     7 import { HappyComponent } from './routes/happy/happy.component';
     8 
     9 
    10 @NgModule({
    11   declarations: [
    12     AppComponent,
    13     WorkComponent,
    14     StudyComponent,
    15     HappyComponent
    16   ],
    17   imports: [
    18     BrowserModule,
    19     AppRoutingModule
    20   ],
    21   providers: [],
    22   bootstrap: [AppComponent]
    23 })
    24 
    25 export class AppModule { }
    app.module.ts
     1 import { NgModule } from '@angular/core';
     2 import { Routes, RouterModule } from '@angular/router';
     3 import { HappyComponent } from './routes/happy/happy.component';
     4 import { StudyComponent } from './routes/study/study.component';
     5 import { WorkComponent } from './routes/work/work.component';
     6 
     7 const routes: Routes = [
     8   { path: '', component: HappyComponent },
     9   { path: 'work', component: WorkComponent },
    10   { path: 'happy', component: HappyComponent },
    11   { path: 'study', component: StudyComponent },
    12   { path: '**', component: HappyComponent },
    13 ];
    14 
    15 @NgModule({
    16   imports: [RouterModule.forRoot(routes)],
    17   exports: [RouterModule]
    18 })
    19 export class AppRoutingModule { }
    app-routing.module.ts

    warnning

    根据上面的内容,我们有两种配置路由的方法,即在路由模块或者在模块内部配置路由,但不要同时在两处都配置。

    Router路由器与Route路由

    路由器是一个调度中心,它是一套规则的列表,能够查询当前URL对应的规则,并呈现出相应的视图.

    路由是列表里面的一个规则,即路由定义,它有很多功能字段,上述列子中:它有一个path字段,表示该路由中的URL路径部分和一个Component字段,表示与该路由相关联的组件

    每个带路由的Angular应用都有一个路由器服务的单例对象,通过路由定义的列表进行配置后使用。

    上述具体的工作流程,可以举例简单描述为:

    • 当浏览器地址栏的URL变化时,路径部分/study满足了列表中path为"study"的这个路由定义,激活对应StudyComponent的实例,显示它的视图

    • 当应用程序请求导航到路径/work时,符合了另外的规则,激活对应视图且展示内容,并将该路径更新到浏览器地址栏和历史

    Warnning

    RouterModule.forRoot方法是用于注册全应用级提供商的编码模式.把RouterModule.forRoot()注册到AppModule的imports中,能让该Router服务在应用的任何地方都能使用.只在根模块AppRoutingModule中调用RouterModule.forRoot(如果在AppModule中注册应用的顶级路由,那就在 AppModule中调用),在其它模块,你就必须调用RouterModule.forChild方法来注册附属路由.

    默认路由与通配符路由

    默认路由就是上述的空路由'',表示应用的默认路径,当URL为空时就会访问那里,因此它通常会作为起点。

    最后一个路由中的**路径是一个通配符,当所请求的URL不匹配前面定义的路由表中的任何路径时,路由器就会选择此路由.这个特性可用于显示“404 - Not Found”页,或自动重定向到其它路由.

    顺序

    路由器中的路由定义列表的顺序是非常重要的,它符合就近原则,当浏览器的URL变化时,Router会从上到下依次查找对应的Route,找到符合规则Route后,就决定显示那个组件,那么后面的路由就被"短路"了,例如,如果把通配符路由放在第一个,那么无论路由怎么变化,在他后面的路由都失去了意义,因为不论怎么变化,他都是第一个符合规则的,所以它通常要放在最后一个

    路由出口

    我们配置好的路由,是在哪里渲染内容呢?

    下列是一个新建项目的app.component.html视图内容:

     1 <!--The content below is only a placeholder and can be replaced.-->
     2 <div style="text-align:center">
     3   <h1>
     4     Welcome to {{ title }}!
     5   </h1>
     6   <img width="300" alt="Angular Logo" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
     7 </div>
     8 <h2>Here are some links to help you start: </h2>
     9 <ul>
    10   <li>
    11     <h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
    12   </li>
    13   <li>
    14     <h2><a target="_blank" rel="noopener" href="https://angular.io/cli">CLI Documentation</a></h2>
    15   </li>
    16   <li>
    17     <h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
    18   </li>
    19 </ul>
    20 
    21 <router-outlet></router-outlet>
    app.component.html

     其中有这样一个标签<router-outlet></router-outlet>

    RouterOutlet是一个来自路由模块中的指令,它的用法类似于组件.它扮演一个占位符的角色,用于在模板中标出一个位置,路由器将会把要显示在这个出口处的组件显示在这里,即在宿主视图中的RouterOutlet之后显示组件内容. 

    路由嵌套

    路由和组件一样,都是树形结构的,可以层层嵌套,配置子路由

    在如上内容的结构下,假设在happy组件中增加text,picture,video三个组件,文件结构如下:

    │  app-routing.module.ts
    │  app.component.html
    │  app.component.ts
    │  app.module.ts
    │  
    └─routes
        │  
        ├─happy
        │  │  happy-routing.module.ts
        │  │  happy.component.html
        │  │  happy.component.ts
        │  │  happy.module.ts
        │  │  
        │  ├─picture
        │  │      picture.component.html
        │  │      picture.component.ts
        │  │      
        │  ├─text
        │  │      text.component.html
        │  │      text.component.ts
        │  │      
        │  └─video
        │          video.component.html
        │          video.component.ts
        │          
        ├─study
        │      study.component.html
        │      study.component.ts
        │      
        └─work
                work.component.html
                work.component.ts
    文件结构

    注意一点我们需要在AppModule中引入HappyModule

    具体代码:

     1 import { BrowserModule } from '@angular/platform-browser';
     2 import { NgModule } from '@angular/core';
     3 import { AppComponent } from './app.component';
     4 import { AppRoutingModule } from './app-routing.module';
     5 import { WorkComponent } from './routes/work/work.component';
     6 import { StudyComponent } from './routes/study/study.component';
     7 import { HappyComponent } from './routes/happy/happy.component';
     8 import { HappyModule } from './routes/happy/happy.module';
     9 
    10 
    11 @NgModule({
    12   declarations: [
    13     AppComponent,
    14     WorkComponent,
    15     StudyComponent,
    16     HappyComponent
    17   ],
    18   imports: [
    19     BrowserModule,
    20     HappyModule,
    21     AppRoutingModule
    22   ],
    23   providers: [],
    24   bootstrap: [AppComponent]
    25 })
    26 
    27 export class AppModule { }
    app.module.ts
     1 import { NgModule } from '@angular/core';
     2 import { Routes, RouterModule } from '@angular/router';
     3 import { HappyComponent } from './routes/happy/happy.component';
     4 import { StudyComponent } from './routes/study/study.component';
     5 import { WorkComponent } from './routes/work/work.component';
     6 
     7 const routes: Routes = [
     8   { path: '', component: HappyComponent },
     9   { path: 'work', component: WorkComponent },
    10   { path: 'happy', component: HappyComponent },
    11   { path: 'study', component: StudyComponent },
    12   { path: '**', component: HappyComponent },
    13 ];
    14 
    15 @NgModule({
    16   imports: [RouterModule.forRoot(routes)],
    17   exports: [RouterModule]
    18 })
    19 export class AppRoutingModule { }
    app-routing.module.ts
     1 import { NgModule } from '@angular/core';
     2 import { CommonModule } from '@angular/common';
     3 import { HappyRoutingModule } from './happy-routing.module';
     4 import { VideoComponent } from './video/video.component';
     5 import { PictureComponent } from './picture/picture.component';
     6 import { TextComponent } from './text/text.component';
     7 
     8 @NgModule({
     9   declarations: [
    10     VideoComponent,
    11     PictureComponent,
    12     TextComponent
    13   ],
    14   imports: [
    15     CommonModule,
    16     HappyRoutingModule
    17   ]
    18 })
    19 export class HappyModule { }
    happy.module.ts
     1 import { NgModule } from '@angular/core';
     2 import { Routes, RouterModule } from '@angular/router';
     3 import { HappyComponent } from './happy.component';
     4 import { TextComponent } from './text/text.component';
     5 import { PictureComponent } from './picture/picture.component';
     6 import { VideoComponent } from './video/video.component';
     7 
     8 const routes: Routes = [
     9   {
    10     path: 'happy',
    11     component: HappyComponent,
    12     children: [
    13       {
    14         path: '',
    15         children: [
    16           {
    17             path: '',
    18             component: TextComponent
    19           },
    20           {
    21             path: 'text',
    22             component: TextComponent
    23           },
    24           {
    25             path: 'picture',
    26             component: PictureComponent
    27           },
    28           {
    29             path: 'video',
    30             component: VideoComponent
    31           },
    32           {
    33             path: '**',
    34             component: TextComponent
    35           }
    36         ]
    37       }
    38     ]
    39   }
    40 ];
    41 
    42 @NgModule({
    43   imports: [RouterModule.forChild(routes)],
    44   exports: [RouterModule]
    45 })
    46 
    47 export class HappyRoutingModule { }
    happy-routing.module.ts

     在子路由下面使用了一次空路由,Router支持空路径路由,可以使用它们来分组路由,而不用往 URL 中添加额外的路径片段

    假设在路由守卫的时候,想对每一个子路由进行认证,这时候就不需要一一添加,加在这个空路由上即可

    路由跳转

    在具体的应用中,我们不可能让所有的路由触发都是靠路由地址的改变来实现,这是非常被动的

    很多情况下,是我们通过事件,主动触发路由的变化,具体内容参见组件通讯,在路由跳转中我已经写明

    路由守卫

    目前,任何用户都能在任何时候导航到任何地方,对于大部分应用,这样是存在安全问题的,某些用户可能无权导航到目标组件,需要先登录(认证)

    在显示目标组件前,可能需要先获取某些数据。

    在离开组件前,可能要先保存修改.需要询问用户:是否要放弃本次更改,而不用保存它们?

    对于上述这些场景问题,往往需要在路由配置中添加守卫,进行处理.

    守卫通过返回一个值,以控制路由器的行为:

    如果它返回 true,导航过程会继续

    如果它返回 false,导航过程就会终止,且用户留在原地。

    如果它返回 UrlTree,则取消当前的导航,并且开始导航到返回的这个 UrlTree.

    warnning

    守卫还可以告诉路由器导航到别处,这样也会取消当前的导航。要想在守卫中这么做,就要返回 false;

    守卫可以用同步的方式返回一个布尔值,但在很多情况下,守卫无法用同步的方式给出答案.守卫可能会向用户问一个问题、把更改保存到服务器,或者获取新数据,而这些都是异步操作。因此,路由的守卫可以返回一个Observable<boolean> Promise<boolean>,并且路由器会等待这个可观察对象被解析为true或false。

    warnning

    提供给路由器的可观察对象还必须能结束,否则,导航就不会继续. 

    路由器可以支持多种守卫接口:

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

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

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

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

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

    在分层路由的每个级别上,你都可以设置多个守卫,上面提到过的空路由,在这里会可能发挥很好的作用

    路由器会先按照从最深的子路由由下往上检查的顺序来检查CanDeactivate() 和CanActivateChild() 守卫.然后它会按照从上到下的顺序检查CanActivate()守卫. 如果特性模块是异步加载的,在加载它之前还会检查CanLoad()守卫. 如果任何一个守卫返回 false,其它尚未完成的守卫会被取消,这样整个导航就被取消.

    下面以路由认证和处理未保存的更改来认识一下路由守卫

    路由认证

    增添一个登陆的功能,在访问/work的时候需要登陆才能访问,否则跳转到登陆页面

    首先添加一个auth的服务,来保存登录状态和登陆与注销的功能

    ng generate service auth/auth    (简写ng g s auth/auth)

    再添加一个auth守卫

    ng generate guard auth/auth    (简写ng g g auth/auth)

    最后在对应路由中添加这个守卫

     1 import { Injectable } from '@angular/core';
     2 import { CanActivate, CanLoad, Route, UrlSegment, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
     3 import { Observable } from 'rxjs';
     4 import { AuthService } from './auth.service';
     5 
     6 @Injectable({
     7   providedIn: 'root'
     8 })
     9 export class AuthGuard implements CanActivate {
    10   constructor(
    11     private authService: AuthService,
    12     private router: Router
    13   ) { }
    14 
    15   canActivate(
    16     next: ActivatedRouteSnapshot,
    17 
    18     state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    19 
    20     let url = state.url
    21 
    22     return this.checkLogin(url);
    23   }
    24 
    25   checkLogin(url: string) {
    26     if (this.authService.isLoggedIn) return true;
    27 
    28     this.authService.redirectUrl = url;
    29 
    30     this.router.navigate(["login"]);
    31 
    32     return false;
    33   }
    34 }
    auth.guard.ts
     1 import { Injectable } from '@angular/core';
     2 import { Observable, of } from 'rxjs';
     3 import { tap, delay } from 'rxjs/operators';
     4 
     5 @Injectable({
     6   providedIn: 'root'
     7 })
     8 export class AuthService {
     9   //是否登陆的状态
    10   isLoggedIn: boolean = false;
    11 
    12   // 登录后重定向的地址
    13   redirectUrl: string = '';
    14 
    15   constructor() { }
    16 
    17   login(): Observable<boolean> {
    18     return of(true).pipe(
    19       delay(1000),
    20       tap(val => this.isLoggedIn = true)
    21     );
    22   }
    23 
    24   logout(): void {
    25     this.isLoggedIn = false;
    26   }
    27 }
    auth.service.ts
    1   { path: 'work', canActivate: [AuthGuard], component: WorkComponent },
    routes-routing.module.ts

    处理未保存的更改

     在某些页面,我们可能会处理类似表单之类的提交数据的操作,有时候在没有完成数据提交之前,我们就有意或无意的离开了当前页面,比如:不小心触发了浏览器后退事件,或者主动的点击某个连接跳出当前页面,放弃此次操作.我们不能把每一次的路由变化都视为有意为之,如果真是不小心跳转的,那么可能填写的很多数据都付诸流水了

    因此,当用户导航在页面之外时,应该弹出一个面板,询问是否离开当前页面, 如果用户选择了取消,就留在当前页面,并允许更多改动.如果用户选择了确认,那就放弃此次操作

    首先,添加一个服务,用来弹出面板,确认用户的操作.

     ng generate service dialog(ng g s dialog)

    为DialogService 添加一个confirm()方法,以提醒用户确认.window.confirm是一个阻塞型操作,它会显示一个模态对话框,并等待用户的交互。

     1 import { Injectable } from '@angular/core';
     2 import { of, Observable } from 'rxjs';
     3 
     4 @Injectable({
     5   providedIn: 'root'
     6 })
     7 export class DialogService {
     8 
     9   confirm(meesage: string): Observable<boolean> {
    10 
    11     const confirmation = window.confirm(meesage || '确认离开吗?')
    12 
    13     return of(confirmation);
    14   }
    15 }
    dialog.service.ts

    然后,生成一个守卫guard,以检查组件(任意组件均可)中是否存在canDeactivate()方法。

    ng generate guard can-deactivate(ng g g can-deactivate)

    对于这个任意组件,守卫只需要检查它是否有一个canDeactivate()方法,并调用它,这就让该守卫可以复用.

     假设用来保存上述StudyComponent组件里面的内容,那么需要修改成如下内容:

     1 import { Component, OnInit } from '@angular/core';
     2 import { Observable } from 'rxjs';
     3 import { DialogService } from '../../dialog/dialog.service';
     4 import { CanComponentDeactivate } from '../../can-deactivate/can-deactivate.guard';
     5 
     6 
     7 @Component({
     8   selector: 'app-study',
     9   templateUrl: './study.component.html'
    10 })
    11 export class StudyComponent implements OnInit, CanComponentDeactivate {
    12   isChange: boolean = true;//提交数据是否发生变化
    13   constructor(
    14     public dialogService: DialogService
    15   ) { }
    16 
    17   ngOnInit() { }
    18 
    19   canDeactivate(): Observable<boolean> | boolean {
    20     if (!this.isChange) return true; //未发生变化
    21     return this.dialogService.confirm('是否离开当前页面?');
    22   }
    23 }
    study.component.ts

    这是通用的情况下,当然也可以为组件创建特定的CanDeactivate守卫,那就把守卫换成一下内容即可

     1 import { Injectable } from '@angular/core';
     2 import { CanDeactivate } from '@angular/router';
     3 import { StudyComponent } from '../routes/study/study.component';
     4 
     5 
     6 @Injectable({
     7   providedIn: 'root',
     8 })
     9 export class CanDeactivateGuard implements CanDeactivate<StudyComponent> {
    10   canDeactivate(component: StudyComponent) {
    11     return component.canDeactivate ? component.canDeactivate() : true;
    12   }
    13 }
    can-deactivate.guard.ts

    当然组件就不需要implements那个通用的接口CanComponentDeactivate了

    最后把这个守卫放在对应的路由定义下就行了

    异步路由

    惰性加载

    随着应用程序的不断壮大,程序的加载时间将会过长,这是我们不得不正视的一个严重问题.

    如何才能解决这个问题呢?最好的办法就是引进异步路由:可以获得在请求时才惰性加载特性模块的能力. 惰性加载有多个优点:

    • 你可以只在用户请求时才加载某些特性区。

    • 对于那些只访问应用程序某些区域的用户,这样能加快加载速度。

    • 你可以持续扩充惰性加载特性区的功能,而不用增加初始加载的包体积。

    惰性加载是加载的模块,所以需要对上述的结构改进一下:

    │  app-routing.module.ts
    │  app.component.html
    │  app.component.ts
    │  app.module.ts
    │             
    └─routes
        │  routes-routing.module.ts
        │  routes.module.ts
        │  
        ├─happy
        │  │  happy-routing.module.ts
        │  │  happy.component.html
        │  │  happy.component.ts
        │  │  happy.module.ts
        │  │  
        │  ├─picture
        │  │      picture.component.html
        │  │      picture.component.ts
        │  │      
        │  ├─text
        │  │      text.component.html
        │  │      text.component.ts
        │  │      
        │  └─video
        │          video.component.html
        │          video.component.ts
        │          
        ├─study
        │      study-routing.module.ts
        │      study.component.html
        │      study.component.ts
        │      study.module.ts
        │      
        └─work
                work-routing.module.ts
                work.component.html
                work.component.ts
                work.module.ts
    文件结构

     具体代码:

     1 import { NgModule } from '@angular/core';
     2 import { Routes, RouterModule } from '@angular/router';
     3 
     4 const routes: Routes = [];
     5 
     6 @NgModule({
     7   imports: [RouterModule.forRoot(routes)],
     8   exports: [RouterModule]
     9 })
    10 
    11 export class AppRoutingModule { }
    app-routing.module.ts
     1 import { BrowserModule } from '@angular/platform-browser';
     2 import { NgModule } from '@angular/core';
     3 import { AppComponent } from './app.component';
     4 import { AppRoutingModule } from './app-routing.module';
     5 import { RoutesModule } from './routes/routes.module';
     6 
     7 
     8 @NgModule({
     9   declarations: [
    10     AppComponent
    11   ],
    12   imports: [
    13     BrowserModule,
    14     RoutesModule,
    15     AppRoutingModule
    16   ],
    17   providers: [],
    18   bootstrap: [AppComponent]
    19 })
    20 
    21 export class AppModule { }
    app.module.ts
     1 import { NgModule } from '@angular/core';
     2 import { Routes, RouterModule } from '@angular/router';
     3 import { HappyComponent } from './happy/happy.component';
     4 
     5 const routes: Routes = [
     6   { path: '', component: HappyComponent },
     7   {
     8     path: 'work',
     9     loadChildren: './work/work.module#WorkModule'
    10   },
    11   {
    12     path: 'study',
    13     loadChildren: './study/study.module#StudyModule'
    14   },
    15   { path: '**', component: HappyComponent },
    16 ]
    17 @NgModule({
    18   imports: [RouterModule.forRoot(routes)],
    19   exports: [RouterModule]
    20 })
    21 export class RoutesRoutingModule { }
    routes-routing.module.ts
     1 import { NgModule } from '@angular/core';
     2 import { CommonModule } from '@angular/common';
     3 import { RoutesRoutingModule } from './routes-routing.module';
     4 import { HappyModule } from './happy/happy.module';
     5 
     6 @NgModule({
     7   declarations: [],
     8   imports: [
     9     CommonModule,
    10     HappyModule,
    11     RoutesRoutingModule
    12   ]
    13 })
    14 export class RoutesModule { }
    routes.module.ts
     1 import { NgModule } from '@angular/core';
     2 import { Routes, RouterModule } from '@angular/router';
     3 import { WorkComponent } from './work.component';
     4 
     5 const routes: Routes = [
     6   { path: '', redirectTo: 'work', pathMatch: 'full' },
     7   { path: 'work', component: WorkComponent }
     8 ];
     9 
    10 @NgModule({
    11   imports: [RouterModule.forChild(routes)],
    12   exports: [RouterModule]
    13 })
    14 export class WorkRoutingModule { }
    work-routing.module.ts
     1 import { NgModule } from '@angular/core';
     2 import { CommonModule } from '@angular/common';
     3 
     4 import { WorkRoutingModule } from './work-routing.module';
     5 import { WorkComponent } from './work.component';
     6 
     7 @NgModule({
     8   declarations: [WorkComponent],
     9   imports: [
    10     CommonModule,
    11     WorkRoutingModule
    12   ]
    13 })
    14 export class WorkModule { }
    work.module.ts
     1 import { NgModule } from '@angular/core';
     2 import { Routes, RouterModule } from '@angular/router';
     3 import { HappyComponent } from './happy.component';
     4 import { TextComponent } from './text/text.component';
     5 import { PictureComponent } from './picture/picture.component';
     6 import { VideoComponent } from './video/video.component';
     7 
     8 const routes: Routes = [
     9   {
    10     path: 'happy',
    11     component: HappyComponent,
    12     children: [
    13       {
    14         path: '',
    15         children: [
    16           {
    17             path: '',
    18             component: TextComponent
    19           },
    20           {
    21             path: 'text',
    22             component: TextComponent
    23           },
    24           {
    25             path: 'picture',
    26             component: PictureComponent
    27           },
    28           {
    29             path: 'video',
    30             component: VideoComponent
    31           },
    32           {
    33             path: '**',
    34             component: TextComponent
    35           }
    36         ]
    37       }
    38     ]
    39   }
    40 ];
    41 
    42 @NgModule({
    43   imports: [RouterModule.forChild(routes)],
    44   exports: [RouterModule]
    45 })
    46 
    47 export class HappyRoutingModule { }
    happy-routing.module.ts
     1 import { NgModule } from '@angular/core';
     2 import { CommonModule } from '@angular/common';
     3 import { HappyRoutingModule } from './happy-routing.module';
     4 import { VideoComponent } from './video/video.component';
     5 import { PictureComponent } from './picture/picture.component';
     6 import { TextComponent } from './text/text.component';
     7 import { HappyComponent } from './happy.component';
     8 
     9 @NgModule({
    10   declarations: [
    11     HappyComponent,
    12     VideoComponent,
    13     PictureComponent,
    14     TextComponent
    15   ],
    16   imports: [
    17     CommonModule,
    18     HappyRoutingModule
    19   ]
    20 })
    21 export class HappyModule { }
    happy.module.ts

    HappyModule是启动后的默认路由模块,需要在启动时加载,所有没有惰性加载

    对于WorkModule和StudyModule,只有在我们访问的时候,它们才会加载,这样就节约了启动时的加载时间

    惰性加载和重新配置工作只会发生一次,也就是在该路由首次被请求时.在后续的请求中,该模块和路由都是立即可用的。

    CanLoad守卫:保护对特性模块的未授权加载

    在上面路由认证那节,我们对/work地址进行了认证,它会阻止未登陆用户访问/work,如果用户未登录,它就会跳转到登录页。

    如果是以模块的形式 那么路由器仍然会加载WorkModule—— 即使用户无法访问它的任何一个组件.理想的方式是,只有在用户已登录的情况下才加载WorkModule。

    使用CanLoad守卫,它只在用户已登录并且/work的时候,才加载 WorkModule一次。

    现有的auth.guard.ts 的checkLogin()方法中已经有了支持CanLoad守卫的基础逻辑。

    打开auth.guard.ts ,从@angular/router 中导入CanLoad接口,把它添加到AuthGuard类的implements列表中.然后实现canLoad,代码如下:

     1 import { Injectable } from '@angular/core';
     2 import { CanActivate, CanLoad, Route, UrlSegment, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, Router } from '@angular/router';
     3 import { Observable } from 'rxjs';
     4 import { AuthService } from './auth.service';
     5 
     6 @Injectable({
     7   providedIn: 'root'
     8 })
     9 export class AuthGuard implements CanActivate,CanLoad {
    10   constructor(
    11     private authService: AuthService,
    12     private router: Router
    13   ) { }
    14 
    15   canActivate(
    16     next: ActivatedRouteSnapshot,
    17 
    18     state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
    19 
    20     let url = state.url
    21 
    22     return this.checkLogin(url);
    23   }
    24 
    25   canLoad(route: Route): boolean {
    26     let url = `/${route.path}`;
    27     return this.checkLogin(url);
    28   }
    29 
    30   checkLogin(url: string) {
    31     if (this.authService.isLoggedIn) return true;
    32 
    33     this.authService.redirectUrl = url;
    34 
    35     this.router.navigate(["login"]);
    36 
    37     return false;
    38   }
    39 }
    auth.guard.ts

    路由器会把canLoad()方法的route参数设置为准备访问的目标URL.如果用户已经登录了,checkLogin()方法就会重定向到那个 URL。

    路由的内容是实在过于丰富,本文只能详尽部分内容,如想了解更多内容,请参见官方文档

    (终)

    文档信息


    感谢您的阅读,如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮。本文欢迎各位转载,但是转载文章之后必须在文章页面中给出作者和原文连接
  • 相关阅读:
    VS中添加搜索路径和链接库的方法
    hive多分隔符支持
    shell 遍历目录下的所有文件
    使用ansible控制Hadoop服务的启动和停止【转】
    Shell中的括号有其特殊的用法
    shell中括号[]的特殊用法 linux if多条件判断
    Linux中rz和sz命令用法详解
    vim 去掉自动注释和自动回车
    ping判断局域网ip使用情况
    shell判断有效日期
  • 原文地址:https://www.cnblogs.com/banluduxing/p/9380697.html
Copyright © 2011-2022 走看看