路由與導(dǎo)航
在用戶(hù)使用應(yīng)用程序時(shí)烈疚,Angular
的路由器能讓用戶(hù)從一個(gè)視圖導(dǎo)航到另一個(gè)視圖谊迄。
概覽
Angular
的 Router
(即“路由器”)把瀏覽器中的 URL
看做一個(gè)操作指南艳狐, 據(jù)此導(dǎo)航到一個(gè)視圖模聋,并可以把參數(shù)傳給支撐視圖的相應(yīng)組件,幫它決定具體該展現(xiàn)哪些內(nèi)容。
路由器還在瀏覽器的歷史日志中記錄下這些活動(dòng)瞳浦,這樣瀏覽器的前進(jìn)和后退按鈕也能照常工作。
基礎(chǔ)知識(shí)
base href
元素废士,來(lái)告訴路由器該如何合成導(dǎo)航用的 URL
叫潦。
需要在index.html
的 <head>
標(biāo)簽下先添加一個(gè) <base>
元素。
src/index.html
<base href="/">
從路由庫(kù)中導(dǎo)入
Angular
的路由器是一個(gè)可選的服務(wù)官硝,它用來(lái)呈現(xiàn)指定的 URL
所對(duì)應(yīng)的視圖矗蕊。 它并不是 Angular
核心庫(kù)的一部分,而是在它自己的 @angular/router
包中氢架。
src/app/app.module.ts
import { RouterModule, Routes } from '@angular/router';
配置
路由器需要先配置才會(huì)有路由信息傻咖。 下面的例子創(chuàng)建了五個(gè)路由定義,并用 <font size=5>RouterModule.forRoot
</font> 方法來(lái)配置路由器岖研, 并把它的返回值添加到 AppModule
的 imports
數(shù)組中卿操。
src/app/app.module.ts
const appRoutes: Routes = [
{ path: 'crisis-center', component: CrisisListComponent },
{ path: 'hero/:id', component: HeroDetailComponent },
{
path: 'heroes',
component: HeroListComponent,
data: { title: 'Heroes List' }
},
{
path:'portal',
loadChildren: './settings/settings.module#SettingsModule'
}
{ path: '',
redirectTo: '/heroes',
pathMatch: 'full'
},
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(
appRoutes,
{ enableTracing: true } // <-- debugging purposes only
)
// other imports here
],
...
})
export class AppModule { }
這里的路由數(shù)組 appRoutes
描述如何進(jìn)行導(dǎo)航。 把它傳給 RouterModule.forRoot
方法并傳給本模塊的 imports
數(shù)組就可以配置路由器孙援。
每個(gè) Route
都會(huì)把一個(gè) URL
的 path
映射到一個(gè)組件害淤。
注意,path
不能以斜杠(/)
開(kāi)頭赃磨,可以以(../)
開(kāi)頭筝家。
第二個(gè)路由中的 :id
是一個(gè)路由參數(shù)的令牌(Token)
。比如 /hero/42
這個(gè) URL
中邻辉,“42”
就是 id
參數(shù)的值溪王。
第三個(gè)路由中的 data
屬性用來(lái)存放于每個(gè)具體路由有關(guān)的任意信息。該數(shù)據(jù)可以被任何一個(gè)激活路由訪問(wèn)值骇,并能用來(lái)保存諸如 頁(yè)標(biāo)題莹菱、面包屑以及其它靜態(tài)只讀數(shù)據(jù)。
第四個(gè)路由中我們沒(méi)有將 ettingsModule
導(dǎo)入到我們的 AppModule
中吱瘩,而是通過(guò) loadChildren
屬性來(lái)告訴 Angular
路由依據(jù) loadChildren
屬性配置的路徑去加載 SettingsModule
模塊道伟。這就是模塊懶加載功能的具體應(yīng)用,當(dāng)用戶(hù)訪問(wèn) /settings/**
路徑的時(shí)候使碾,才會(huì)加載對(duì)應(yīng)的 SettingsModule
模塊蜜徽,這減少了應(yīng)用啟動(dòng)時(shí)加載資源的大小。
另外我們傳遞一個(gè)字符串作為 loadChildren
的屬性值票摇,該字符串由三部分組成:
(1)需要導(dǎo)入模塊的相對(duì)路徑
(2)#
分隔符
(3)導(dǎo)出模塊類(lèi)的名稱(chēng)
第五個(gè)路由中的空路徑('')
表示應(yīng)用的默認(rèn)路徑拘鞋,當(dāng) URL
為空時(shí)就會(huì)訪問(wèn)那里,因此它通常會(huì)作為起點(diǎn)矢门。 這個(gè)默認(rèn)路由會(huì)重定向到 URL
/heroes
盆色,并顯示 HeroesListComponent
灰蛙。
最后一個(gè)路由中的 ** 路徑是一個(gè)通配符。當(dāng)所請(qǐng)求的 URL
不匹配前面定義的路由表中的任何路徑時(shí)隔躲,路由器就會(huì)選擇此路由摩梧。 這個(gè)特性可用于顯示“404 - Not Found”
頁(yè),或自動(dòng)重定向到其它路由宣旱。
這些路由的定義順序是刻意如此設(shè)計(jì)的仅父。路由器使用先匹配者優(yōu)先的策略來(lái)匹配路由,所以响鹃,具體路由應(yīng)該放在通用路由的前面驾霜。在上面的配置中,帶靜態(tài)路徑的路由被放在了前面买置,后面是空路徑路由,因此它會(huì)作為默認(rèn)路由强霎。而通配符路由被放在最后面忿项,這是因?yàn)樗芷ヅ渖厦恳粋€(gè) URL
,因此應(yīng)該只有在前面找不到其它能匹配的路由時(shí)才匹配它城舞。
如果你想要看到在導(dǎo)航的生命周期中發(fā)生過(guò)哪些事件轩触,可以使用路由器默認(rèn)配置中的 enableTracing
選項(xiàng)。它會(huì)把每個(gè)導(dǎo)航生命周期中的事件輸出到瀏覽器的控制臺(tái)家夺。 這應(yīng)該只用于調(diào)試脱柱。你只需要把 enableTracing: true
選項(xiàng)作為第二個(gè)參數(shù)傳給 RouterModule.forRoot()
方法就可以了。
路由數(shù)組
Routes
是路由配置數(shù)組拉馋。每個(gè)都有以下屬性:
-
path
是路由匹配的路徑榨为。 -
pathMatch
是指定匹配策略的字符串。pathMatch:'full'
表示完全匹配 -
matcher
定義了路徑匹配并取代自定義策略path
和pathMatch
煌茴。 -
component
是組件類(lèi)型随闺。 -
redirectTo
是將替換當(dāng)前匹配段的url
片段。 -
outlet
是組件應(yīng)放入的插座的名稱(chēng)蔓腐。 -
canActivate
控制是否允許進(jìn)入路由矩乐。。 -
canActivateChild
等同canActivate
回论,只不過(guò)針對(duì)是所有子路由散罕。。 -
canDeactivate
控制是否允許離開(kāi)路由傀蓉。 -
canLoad
控制是否允許延遲加載整個(gè)模塊欧漱。 -
data
是提供給組件的附加數(shù)據(jù),被激活路由訪問(wèn)僚害。 -
resolve
是用于查找數(shù)據(jù)解析器的DI
令牌的映射硫椰。 -
children
是子路由定義的數(shù)組繁调。 -
loadChildren
是對(duì)延遲加載子路由的引用。
注意:路由守衛(wèi)對(duì)于權(quán)限控制非常便利靶草,當(dāng)然其粒度當(dāng)然只能在頁(yè)面層級(jí)蹄胰。倘若需要對(duì)按鈕粒度也只能利用指令的方式,而二者的結(jié)合可以極大的改善權(quán)限控制埋點(diǎn)的代碼量奕翔。
RouterModule.forChild()
RouterModule.forChild()
與 Router.forRoot()
方法類(lèi)似裕寨,但它只能應(yīng)用在特性模塊中。
- 友情提示:根模塊中使用 forRoot()派继,子模塊中使用 forChild()
這個(gè)功能非常強(qiáng)大宾袜,因?yàn)槲覀儾槐卦谝粋€(gè)地方(我們的主模塊)定義所有路由信息。反之驾窟,我們可以在特性模塊中定義模塊特有的路由信息庆猫,并在必要的時(shí)候?qū)⑺鼈儗?dǎo)入我們主模塊。RouterModule.forChild()
的使用方法如下:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Routes, RouterModule } from '@angular/router';
export const ROUTES: Routes = [
{
path: 'settings',
component: SettingsComponent,
///settings 設(shè)置頁(yè)面下有 /settings/profile 和 /settings/password 兩個(gè)頁(yè)面
children: [
{ path: 'profile', component: ProfileSettingsComponent },
{ path: 'password', component: PasswordSettingsComponent }
]
}
];
@NgModule({
imports: [
CommonModule,
RouterModule.forChild(ROUTES)
],
// ...
})
export class ChildModule {}
通過(guò)以上示例绅络,我們知道在主模塊和特性模塊中月培,路由配置對(duì)象的類(lèi)型是一樣的,區(qū)別只是主模塊和特性模塊中需調(diào)用不同的方法恩急,來(lái)配置模塊路由杉畜。
路由出口
RouterOutlet
是一個(gè)來(lái)自路由模塊中的指令,它的用法類(lèi)似于組件衷恭。 它扮演一個(gè)占位符的角色此叠,用于在模板中標(biāo)出一個(gè)位置,路由器將會(huì)把要顯示在這個(gè)出口處的組件顯示在這里随珠。
<router-outlet></router-outlet>
<!-- Routed components go here -->
有了這份配置灭袁,當(dāng)本應(yīng)用在瀏覽器中的 URL
變?yōu)?/heroes
時(shí),路由器就會(huì)匹配到 path
為 heroes
的 Route
牙丽,并在宿主視圖中的RouterOutlet
之后顯示 HeroListComponent
組件简卧。
- 多個(gè)路由區(qū)域
1.路由配置
const routes: Routes = [
{ path: 'news',
component: NewsComponent,
outlet:'let1'
}
{ path: 'news',
component: News2Cmponent,
outlet:'let2'
}]
2.html點(diǎn)擊鏈接
<a routerLink = "[{ outlets: { let1: ['news'] } }]"></a>
<a routerLink = "[{ outlets: { let2: ['news'] } }]"></a
3.html路由出口
<router-outlet name="let1"></router-outlet>
<router-outlet name="let2"></router-outlet>
即訪問(wèn) /news/
時(shí)同時(shí)加載 NewsComponent
和 News2Cmponent
兩個(gè)組件
路由器鏈接
現(xiàn)在,你已經(jīng)有了配置好的一些路由烤芦,還找到了渲染它們的地方举娩,但又該如何導(dǎo)航到它呢?固然构罗,從瀏覽器的地址欄直接輸入 URL
也能做到铜涉,但是大多數(shù)情況下,導(dǎo)航是某些用戶(hù)操作的結(jié)果遂唧,比如點(diǎn)擊一個(gè) A
標(biāo)簽芙代。
考慮下列模板:
src/app/app.component.html
<h1>Angular Router</h1>
<nav>
<a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>
<a routerLink="/heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
a
標(biāo)簽上的 <font size=5>RouterLink
</font> 指令讓路由器得以控制這個(gè) a
元素。 這里的導(dǎo)航路徑是固定的盖彭,因此可以把一個(gè)字符串賦給 routerLink
(“一次性”綁定)纹烹。
routerLink
第一個(gè)路徑片段可以以 /
页滚,./
或 ../
開(kāi)頭:
如果以
/
開(kāi)頭,路由將從根路由開(kāi)始查找如果以
./
開(kāi)頭或沒(méi)有使用/
铺呵,則路由將從當(dāng)前激活路由的子路由開(kāi)始查找如果以
../
開(kāi)頭裹驰,路由往上一級(jí)查找
如果需要更加動(dòng)態(tài)的導(dǎo)航路徑,那就把它綁定到一個(gè)返回鏈接參數(shù)數(shù)組的模板表達(dá)式片挂。 路由器會(huì)把這個(gè)數(shù)組解析成完整的 URL
幻林。
例如使用 ['/team', teamId, 'user', userName, {details: true}]
數(shù)組,意味著我們想要生成一個(gè)鏈接到 /team/11/user/bob;details=true
音念。
- ts中跳轉(zhuǎn)寫(xiě)法
import { Router } from '@angular/router';
// ...
constructor(private router: Router) {}
// ...
this.router.navigate(['/detail', this.news.id])
this.router.navigate([{ outlets: { let2: null }}]);
navigateByUrl
方法指向完整的絕對(duì)路徑
路由鏈接的激活狀態(tài)
<a routerLink="/user/bob" routerLinkActive="active">Bob</a>
RouterLinkActive
指令:當(dāng) URL
地址是 /user
或 /user/bob
時(shí)沪饺,當(dāng)前的 RouterState
為活動(dòng)狀態(tài),active
類(lèi)將會(huì)被添加到 <a>
標(biāo)簽上闷愤。如果 URL
發(fā)生變化整葡,則 active
類(lèi)將自動(dòng)從 <a>
標(biāo)簽上移除。
路由鏈接的激活狀態(tài)會(huì)向下級(jí)聯(lián)到路由樹(shù)中的每個(gè)層級(jí)肝谭,所以掘宪,父子路由鏈接可能會(huì)同時(shí)激活。
只有當(dāng) URL
與當(dāng)前 URL
精確匹配時(shí)才會(huì)激活攘烛,可以把 [routerLinkActiveOptions]
綁定為 { exact: true }
表達(dá)式。
路由器狀態(tài)
路由器的當(dāng)前狀態(tài)(RouterState
):在導(dǎo)航時(shí)的每個(gè)生命周期成功完成時(shí)镀首,路由器會(huì)構(gòu)建出一個(gè) ActivatedRoute
組成的樹(shù)坟漱。
你可以在應(yīng)用中的任何地方用 Router
服務(wù)及其 routerState
屬性來(lái)訪問(wèn)當(dāng)前的 RouterState
值。
RouterState
中的每個(gè) ActivatedRoute
都提供了從任意激活路由開(kāi)始向上或向下遍歷路由樹(shù)的一種方式更哄,以獲得關(guān)于父芋齿、子、兄弟路由的信息成翩。
class MyComponent {
constructor(router: Router) {
const state: RouterState = router.routerState;
const snapshot: RouterStateSnapshot = state.snapshot;
const root: ActivatedRouteSnapshot = snapshot.root;
const child = root.firstChild;
const id: Observable<string> = child.params.map(p => p.id);
//...
}
}
激活的路由
該路由的路徑和參數(shù)可以通過(guò)注入進(jìn)來(lái)的一個(gè)名叫ActivatedRoute
的路由服務(wù)來(lái)獲取觅捆。 它有一大堆有用的信息,包括:
屬性 | 說(shuō)明 |
---|---|
url |
路由路徑的 Observable 對(duì)象麻敌,是一個(gè)由路由路徑中的各個(gè)部分組成的字符串?dāng)?shù)組栅炒。 |
data |
一個(gè) Observable ,其中包含提供給路由的 data 對(duì)象术羔。也包含由解析守衛(wèi)(resolve guard )解析而來(lái)的值赢赊。 |
paramMap |
一個(gè) Observable ,其中包含一個(gè)由當(dāng)前路由的必要參數(shù)和可選參數(shù)組成的map 對(duì)象级历。用這個(gè) map 可以獲取來(lái)自同名參數(shù)的單一值或多重值释移。 |
queryParamMap |
一個(gè) Observable ,其中包含一個(gè)對(duì)所有路由都有效的查詢(xún)參數(shù)組成的map 對(duì)象寥殖。 用這個(gè) map 可以獲取來(lái)自查詢(xún)參數(shù)的單一值或多重值玩讳。 |
fragment |
一個(gè)適用于所有路由的 URL 的 fragment (片段)的 Observable 涩蜘。 |
outlet |
要把該路由渲染到的 RouterOutlet 的名字。對(duì)于無(wú)名路由熏纯,它的路由名是 primary 同诫,而不是空串。 |
routeConfig |
用于該路由的路由配置信息豆巨,其中包含原始路徑剩辟。 |
parent |
當(dāng)該路由是一個(gè)子路由時(shí),表示該路由的父級(jí) ActivatedRoute 往扔。 |
firstChild |
包含該路由的子路由列表中的第一個(gè) ActivatedRoute 贩猎。 |
children |
包含當(dāng)前路由下所有已激活的子路由。 |
//獲取路由參數(shù)
private route: ActivatedRoute,
this.username = this.route
.queryParamMap
.pipe(map(params => this.username = params.username));
路由事件
在每次導(dǎo)航中萍膛,Router
都會(huì)通過(guò) Router.events
屬性發(fā)布一些導(dǎo)航事件吭服。這些事件的范圍涵蓋了從開(kāi)始導(dǎo)航到結(jié)束導(dǎo)航之間的很多時(shí)間點(diǎn)。下表中列出了全部導(dǎo)航事件:
路由器事件 | 說(shuō)明 |
---|---|
NavigationStart |
本事件會(huì)在導(dǎo)航開(kāi)始時(shí)觸發(fā)蝗罗。 |
RouteConfigLoadStart |
本事件會(huì)在 Router 惰性加載 某個(gè)路由配置之前觸發(fā)艇棕。 |
RouteConfigLoadEnd |
本事件會(huì)在惰性加載了某個(gè)路由后觸發(fā)。 |
RoutesRecognized |
本事件會(huì)在路由器解析完 URL 串塑,并識(shí)別出了相應(yīng)的路由時(shí)觸發(fā) |
`GuardsCheckStart |
本事件會(huì)在路由器開(kāi)始 Guard` 階段之前觸發(fā)沼琉。 |
ChildActivationStart |
本事件會(huì)在路由器開(kāi)始激活路由的子路由時(shí)觸發(fā)。 |
ActivationStart |
本事件會(huì)在路由器開(kāi)始激活某個(gè)路由時(shí)觸發(fā)桩匪。 |
GuardsCheckEnd |
本事件會(huì)在路由器成功完成了 Guard 階段時(shí)觸發(fā)打瘪。 |
ResolveStart |
本事件會(huì)在 Router 開(kāi)始解析(Resolve )階段時(shí)觸發(fā)。 |
ResolveEnd |
本事件會(huì)在路由器成功完成了路由的解析(Resolve )階段時(shí)觸發(fā)傻昙。 |
ChildActivationEnd |
本事件會(huì)在路由器激活了路由的子路由時(shí)觸發(fā)闺骚。 |
ActivationEnd |
本事件會(huì)在路由器激活了某個(gè)路由時(shí)觸發(fā)。 |
NavigationEnd |
本事件會(huì)在導(dǎo)航成功結(jié)束之后觸發(fā)妆档。 |
NavigationCancel |
本事件會(huì)在導(dǎo)航被取消之后觸發(fā)僻爽。 這可能是因?yàn)樵趯?dǎo)航期間某個(gè)路由守衛(wèi)返回了 false 。 |
NavigationError |
這個(gè)事件會(huì)在導(dǎo)航由于意料之外的錯(cuò)誤而失敗時(shí)觸發(fā)贾惦。 |
Scroll |
本事件代表一個(gè)滾動(dòng)事件胸梆。 |
當(dāng)啟用了 enableTracing
選項(xiàng)時(shí),這些事件也同時(shí)會(huì)記錄到控制臺(tái)中纤虽。要想查看對(duì)路由導(dǎo)航事件進(jìn)行過(guò)濾的例子乳绕,請(qǐng)?jiān)L問(wèn) Angular
中的可觀察對(duì)象一章的路由器部分
路由守衛(wèi)
適用于后臺(tái)管理等需要登錄才能使用的模塊
- 創(chuàng)建一個(gè)認(rèn)證服務(wù)
// app/auth.service.ts
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
@Injectable()
export class AuthService implements CanActivate {
canActivate() {
// 這里判斷登錄狀態(tài), 返回 true 或 false
return true;
}
}
- 添加或修改路由配置
// app/app.router.ts
// 增加 CanActivate
import { CanActivate ... } from '@angular/router';
// 配置中增加 canActivate 如:
{ path: 'admin', canActivate:[AuthService] ... }
總結(jié)一下
該應(yīng)用有一個(gè)配置過(guò)的路由器。 外殼組件中有一個(gè) RouterOutlet
逼纸,它能顯示路由器所生成的視圖洋措。 它還有一些 RouterLink
,用戶(hù)可以點(diǎn)擊它們杰刽,來(lái)通過(guò)路由器進(jìn)行導(dǎo)航菠发。
下面是一些路由器中的關(guān)鍵詞匯及其含義:
路由器部件 | 含義 |
---|---|
Router (路由器) |
為激活的 URL 顯示應(yīng)用組件王滤。管理從一個(gè)組件到另一個(gè)組件的導(dǎo)航。ts->this.router.navigateByUrl("/protel")
|
RouterModule |
一個(gè)獨(dú)立的 Angular 模塊滓鸠,用于提供所需的服務(wù)提供商雁乡,以及用來(lái)在應(yīng)用視圖之間進(jìn)行導(dǎo)航的指令。ts->RouterModule.forRoot(Routers數(shù)組糜俗,ExtraOptions對(duì)象)
|
Routes (路由數(shù)組) |
定義了一個(gè)路由數(shù)組踱稍,每一個(gè)都會(huì)把一個(gè) URL 路徑映射到一個(gè)組件。ts->[(path:'',componet: ***)]
|
Route (路由) |
定義路由器該如何根據(jù) URL 模式(pattern )來(lái)導(dǎo)航到組件悠抹。大多數(shù)路由都由路徑和組件類(lèi)構(gòu)成珠月。 |
RouterOutlet (路由出口) |
該指令(<router-outlet> )用來(lái)標(biāo)記出路由器該在哪里顯示視圖。 |
RouterLink (路由鏈接) |
這個(gè)指令把可點(diǎn)擊的 HTML 元素綁定到某個(gè)路由楔敌。點(diǎn)擊帶有 routerLink 指令(綁定到字符串或鏈接參數(shù)數(shù)組)的元素時(shí)就會(huì)觸發(fā)一次導(dǎo)航啤挎。html-><a [routerLink]="[./order]"></a>
|
RouterLinkActive (活動(dòng)路由鏈接) |
當(dāng) HTML 元素上或元素內(nèi)的routerLink 變?yōu)榧せ罨蚍羌せ顮顟B(tài)時(shí),該指令為這個(gè) HTML 元素添加或移除 CSS 類(lèi)卵凑。html中
|
ActivatedRoute (激活的路由) |
為每個(gè)路由組件提供的一個(gè)服務(wù)庆聘,它包含特定于路由的信息,比如路由參數(shù)勺卢、靜態(tài)數(shù)據(jù)伙判、解析數(shù)據(jù)、全局查詢(xún)參數(shù)和全局碎片(fragment )黑忱。ts中
|
RouterState (路由器狀態(tài)) |
路由器的當(dāng)前狀態(tài)包含了一棵由程序中激活的路由構(gòu)成的樹(shù)澳腹。它包含一些用于遍歷路由樹(shù)的快捷方法。 |
鏈接參數(shù)數(shù)組 | 這個(gè)數(shù)組會(huì)被路由器解釋成一個(gè)路由操作指南杨何。你可以把一個(gè)RouterLink 綁定到該數(shù)組,或者把它作為參數(shù)傳給Router.navigate 方法沥邻。 |
路由組件 | 一個(gè)帶有RouterOutlet 的 Angular 組件危虱,它根據(jù)路由器的導(dǎo)航來(lái)顯示相應(yīng)的視圖。 |