angular6.x系列的學習筆記記錄,仍在不斷完善中,學習地址:
系列目錄
(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 { }

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 { }
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>
其中有這樣一個標簽<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 { }

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

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

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 { }
在子路由下面使用了一次空路由,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 }

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 }

1 { path: 'work', canActivate: [AuthGuard], component: WorkComponent },
處理未保存的更改
在某些頁面,我們可能會處理類似表單之類的提交數據的操作,有時候在沒有完成數據提交之前,我們就有意或無意的離開了當前頁面,比如:不小心觸發了瀏覽器后退事件,或者主動的點擊某個連接跳出當前頁面,放棄此次操作.我們不能把每一次的路由變化都視為有意為之,如果真是不小心跳轉的,那么可能填寫的很多數據都付諸流水了
因此,當用戶導航在頁面之外時,應該彈出一個面板,詢問是否離開當前頁面, 如果用戶選擇了取消,就留在當前頁面,並允許更多改動.如果用戶選擇了確認,那就放棄此次操作
首先,添加一個服務,用來彈出面板,確認用戶的操作.
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 }
然后,生成一個守衛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 }
這是通用的情況下,當然也可以為組件創建特定的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 }
當然組件就不需要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 { }

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

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

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

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

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

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

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 { }
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 }
路由器會把canLoad()方法的route參數設置為准備訪問的目標URL.如果用戶已經登錄了,checkLogin()方法就會重定向到那個 URL。
路由的內容是實在過於豐富,本文只能詳盡部分內容,如想了解更多內容,請參見官方文檔
(終)
文檔信息
- 發表作者: 半路獨行
- 發表出處: 博客園
- 原文地址: https://www.cnblogs.com/banluduxing/p/9380697.html
- 版權信息:
本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可。
感謝您的閱讀,如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕。本文歡迎各位轉載,但是轉載文章之后必須在文章頁面中給出作者和原文連接。