Angular6 學習筆記——路由詳解

angular6.x系列的學習筆記記錄,仍在不斷完善中,學習地址:html

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

http://www.ngfans.net/topic/12/post/2瀏覽器

 

系列目錄

(1)組件詳解之模板語法安全

(2)組件詳解之組件通信服務器

(3)內容投影, ViewChild和ContentChildapp

(4)指令異步

(5)路由ide

 路由存在的意義

通常而言,瀏覽器具備下列導航模式:svg

    在地址欄輸入 URL,瀏覽器就會導航到相應的頁面。oop

    在頁面中點擊連接,瀏覽器就會導航到一個新頁面。

    點擊瀏覽器的前進和後退按鈕,瀏覽器就會在你的瀏覽歷史中向前或向後導航。

那麼,在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。

 

路由的內容是實在過於豐富,本文只能詳盡部份內容,如想了解更多內容,請參見官方文檔

(終)

 

文檔信息

 


感謝您的閱讀,若是您以爲閱讀本文對您有幫助,請點一下「推薦」按鈕。本文歡迎各位轉載,可是轉載文章以後必須在文章頁面中給出做者和原文鏈接

can-deactivate

相關文章
相關標籤/搜索