Angular-路由

路由與導(dǎo)航

在用戶使用應(yīng)用程序時(shí),Angular 的路由器能讓用戶從一個(gè)視圖導(dǎo)航到另一個(gè)視圖。

概覽

AngularRouter(即“路由器”)把瀏覽器中的 URL 看做一個(gè)操作指南, 據(jù)此導(dǎo)航到一個(gè)視圖,并可以把參數(shù)傳給支撐視圖的相應(yīng)組件,幫它決定具體該展現(xiàn)哪些內(nèi)容。
路由器還在瀏覽器的歷史日志中記錄下這些活動,這樣瀏覽器的前進(jìn)和后退按鈕也能照常工作。

基礎(chǔ)知識

base href元素,來告訴路由器該如何合成導(dǎo)航用的 URL
需要在index.html<head> 標(biāo)簽下先添加一個(gè) <base> 元素。
src/index.html

<base href="/">

從路由庫中導(dǎo)入

Angular 的路由器是一個(gè)可選的服務(wù),它用來呈現(xiàn)指定的 URL 所對應(yīng)的視圖。 它并不是 Angular 核心庫的一部分,而是在它自己的 @angular/router 包中。

src/app/app.module.ts

import { RouterModule, Routes } from '@angular/router';

配置

路由器需要先配置才會有路由信息。 下面的例子創(chuàng)建了五個(gè)路由定義,并用 <font size=5>RouterModule.forRoot</font> 方法來配置路由器, 并把它的返回值添加到 AppModuleimports 數(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 都會把一個(gè) URLpath 映射到一個(gè)組件。
注意,path 不能以斜杠(/)開頭,可以以(../)開頭。

第二個(gè)路由中:id 是一個(gè)路由參數(shù)的令牌(Token)。比如 /hero/42 這個(gè) URL 中,“42”就是 id 參數(shù)的值。

第三個(gè)路由中data 屬性用來存放于每個(gè)具體路由有關(guān)的任意信息。該數(shù)據(jù)可以被任何一個(gè)激活路由訪問,并能用來保存諸如 頁標(biāo)題、面包屑以及其它靜態(tài)只讀數(shù)據(jù)。

第四個(gè)路由中我們沒有將 ettingsModule導(dǎo)入到我們的 AppModule 中,而是通過 loadChildren 屬性來告訴 Angular 路由依據(jù) loadChildren 屬性配置的路徑去加載 SettingsModule 模塊。這就是模塊懶加載功能的具體應(yīng)用,當(dāng)用戶訪問 /settings/** 路徑的時(shí)候,才會加載對應(yīng)的 SettingsModule 模塊,這減少了應(yīng)用啟動時(shí)加載資源的大小。
另外我們傳遞一個(gè)字符串作為 loadChildren 的屬性值,該字符串由三部分組成:
(1)需要導(dǎo)入模塊的相對路徑
(2)# 分隔符
(3)導(dǎo)出模塊類的名稱

第五個(gè)路由中的空路徑('')表示應(yīng)用的默認(rèn)路徑,當(dāng) URL 為空時(shí)就會訪問那里,因此它通常會作為起點(diǎn)。 這個(gè)默認(rèn)路由會重定向URL /heroes,并顯示 HeroesListComponent。

最后一個(gè)路由中的 ** 路徑是一個(gè)通配符。當(dāng)所請求的 URL 不匹配前面定義的路由表中的任何路徑時(shí),路由器就會選擇此路由。 這個(gè)特性可用于顯示“404 - Not Found”頁,或自動重定向到其它路由。

這些路由的定義順序是刻意如此設(shè)計(jì)的。路由器使用先匹配者優(yōu)先的策略來匹配路由,所以,具體路由應(yīng)該放在通用路由前面。在上面的配置中,帶靜態(tài)路徑的路由被放在了前面,后面是空路徑路由,因此它會作為默認(rèn)路由。而通配符路由被放在最后面,這是因?yàn)樗芷ヅ渖厦恳粋€(gè) URL,因此應(yīng)該只有在前面找不到其它能匹配的路由時(shí)才匹配它。

如果你想要看到在導(dǎo)航的生命周期中發(fā)生過哪些事件,可以使用路由器默認(rèn)配置中的 enableTracing 選項(xiàng)。它會把每個(gè)導(dǎo)航生命周期中的事件輸出到瀏覽器的控制臺。 這應(yīng)該只用于調(diào)試。你只需要把 enableTracing: true 選項(xiàng)作為第二個(gè)參數(shù)傳給 RouterModule.forRoot()方法就可以了。

路由數(shù)組

Routes是路由配置數(shù)組。每個(gè)都有以下屬性:

  • path 是路由匹配的路徑。
  • pathMatch 是指定匹配策略的字符串。pathMatch:'full'表示完全匹配
  • matcher定義了路徑匹配并取代自定義策略pathpathMatch。
  • component 是組件類型。
  • redirectTo 是將替換當(dāng)前匹配段的url片段。
  • outlet 是組件應(yīng)放入的插座的名稱。
  • canActivate控制是否允許進(jìn)入路由。。
  • canActivateChild等同 canActivate,只不過針對是所有子路由。。
  • canDeactivate控制是否允許離開路由。
  • canLoad控制是否允許延遲加載整個(gè)模塊。
  • data是提供給組件的附加數(shù)據(jù),被激活路由訪問。
  • resolve是用于查找數(shù)據(jù)解析器的DI令牌的映射。
  • children 是子路由定義的數(shù)組。
  • loadChildren是對延遲加載子路由的引用。

注意:路由守衛(wèi)對于權(quán)限控制非常便利,當(dāng)然其粒度當(dāng)然只能在頁面層級。倘若需要對按鈕粒度也只能利用指令的方式,而二者的結(jié)合可以極大的改善權(quán)限控制埋點(diǎn)的代碼量。

RouterModule.forChild()

RouterModule.forChild()Router.forRoot() 方法類似,但它只能應(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è)置頁面下有 /settings/profile 和 /settings/password 兩個(gè)頁面
    children: [
      { path: 'profile', component: ProfileSettingsComponent },
      { path: 'password', component: PasswordSettingsComponent }
    ]
  }
];


@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild(ROUTES)
  ],
  // ...
})
export class ChildModule {}

通過以上示例,我們知道在主模塊和特性模塊中,路由配置對象的類型是一樣的,區(qū)別只是主模塊和特性模塊中需調(diào)用不同的方法,來配置模塊路由。

路由出口

RouterOutlet 是一個(gè)來自路由模塊中的指令,它的用法類似于組件。 它扮演一個(gè)占位符的角色,用于在模板中標(biāo)出一個(gè)位置,路由器將會把要顯示在這個(gè)出口處的組件顯示在這里。

<router-outlet></router-outlet>
<!-- Routed components go here -->

有了這份配置,當(dāng)本應(yīng)用在瀏覽器中的 URL 變?yōu)?/heroes 時(shí),路由器就會匹配到 pathheroesRoute,并在宿主視圖中的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>

即訪問 /news/ 時(shí)同時(shí)加載 NewsComponentNews2Cmponent 兩個(gè)組件

路由器鏈接

現(xiàn)在,你已經(jīng)有了配置好的一些路由,還找到了渲染它們的地方,但又該如何導(dǎo)航到它呢?固然,從瀏覽器的地址欄直接輸入 URL 也能做到,但是大多數(shù)情況下,導(dǎo)航是某些用戶操作的結(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è)路徑片段可以以 / ,./../ 開頭:

  • 如果以 / 開頭,路由將從根路由開始查找

  • 如果以 ./ 開頭或沒有使用 / ,則路由將從當(dāng)前激活路由的子路由開始查找

  • 如果以 ../ 開頭,路由往上一級查找

如果需要更加動態(tài)的導(dǎo)航路徑,那就把它綁定到一個(gè)返回鏈接參數(shù)數(shù)組的模板表達(dá)式。 路由器會把這個(gè)數(shù)組解析成完整的 URL
例如使用 ['/team', teamId, 'user', userName, {details: true}] 數(shù)組,意味著我們想要生成一個(gè)鏈接到 /team/11/user/bob;details=true 。

  • ts中跳轉(zhuǎn)寫法
import { Router } from '@angular/router';
// ...
constructor(private router: Router) {}

// ...

this.router.navigate(['/detail', this.news.id])
this.router.navigate([{ outlets: { let2: null }}]);

navigateByUrl 方法指向完整的絕對路徑

路由鏈接的激活狀態(tài)

<a routerLink="/user/bob" routerLinkActive="active">Bob</a>

RouterLinkActive 指令:當(dāng) URL 地址是 /user/user/bob 時(shí),當(dāng)前的 RouterState 為活動狀態(tài),active 類將會被添加到 <a> 標(biāo)簽上。如果 URL 發(fā)生變化,則 active類將自動從 <a> 標(biāo)簽上移除。

路由鏈接的激活狀態(tài)會向下級聯(lián)到路由樹中的每個(gè)層級,所以,父子路由鏈接可能會同時(shí)激活。
只有當(dāng) URL 與當(dāng)前 URL 精確匹配時(shí)才會激活,可以把 [routerLinkActiveOptions] 綁定為 { exact: true } 表達(dá)式。

路由器狀態(tài)

路由器的當(dāng)前狀態(tài)(RouterState):在導(dǎo)航時(shí)的每個(gè)生命周期成功完成時(shí),路由器會構(gòu)建出一個(gè) ActivatedRoute 組成的。
你可以在應(yīng)用中的任何地方用 Router 服務(wù)及其 routerState 屬性來訪問當(dāng)前的 RouterState 值。

RouterState 中的每個(gè) ActivatedRoute 都提供了從任意激活路由開始向上或向下遍歷路由樹的一種方式,以獲得關(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ù)可以通過注入進(jìn)來的一個(gè)名叫ActivatedRoute路由服務(wù)來獲取。 它有一大堆有用的信息,包括:

屬性 說明
url 路由路徑的 Observable 對象,是一個(gè)由路由路徑中的各個(gè)部分組成的字符串?dāng)?shù)組。
data 一個(gè) Observable,其中包含提供給路由的 data 對象。也包含由解析守衛(wèi)(resolve guard)解析而來的值。
paramMap 一個(gè) Observable,其中包含一個(gè)由當(dāng)前路由的必要參數(shù)和可選參數(shù)組成的map對象。用這個(gè) map可以獲取來自同名參數(shù)的單一值或多重值。
queryParamMap 一個(gè) Observable,其中包含一個(gè)對所有路由都有效的查詢參數(shù)組成的map對象。 用這個(gè) map 可以獲取來自查詢參數(shù)的單一值或多重值。
fragment 一個(gè)適用于所有路由的 URLfragment(片段)的 Observable
outlet 要把該路由渲染到的 RouterOutlet 的名字。對于無名路由,它的路由名是 primary,而不是空串。
routeConfig 用于該路由的路由配置信息,其中包含原始路徑。
parent 當(dāng)該路由是一個(gè)子路由時(shí),表示該路由的父級 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 都會通過 Router.events 屬性發(fā)布一些導(dǎo)航事件。這些事件的范圍涵蓋了從開始導(dǎo)航到結(jié)束導(dǎo)航之間的很多時(shí)間點(diǎn)。下表中列出了全部導(dǎo)航事件:

路由器事件 說明
NavigationStart 本事件會在導(dǎo)航開始時(shí)觸發(fā)。
RouteConfigLoadStart 本事件會在 Router 惰性加載 某個(gè)路由配置之前觸發(fā)。
RouteConfigLoadEnd 本事件會在惰性加載了某個(gè)路由后觸發(fā)。
RoutesRecognized 本事件會在路由器解析完 URL,并識別出了相應(yīng)的路由時(shí)觸發(fā)
`GuardsCheckStart 本事件會在路由器開始Guard` 階段之前觸發(fā)。
ChildActivationStart 本事件會在路由器開始激活路由的子路由時(shí)觸發(fā)。
ActivationStart 本事件會在路由器開始激活某個(gè)路由時(shí)觸發(fā)。
GuardsCheckEnd 本事件會在路由器成功完成了 Guard 階段時(shí)觸發(fā)。
ResolveStart 本事件會在 Router 開始解析(Resolve)階段時(shí)觸發(fā)。
ResolveEnd 本事件會在路由器成功完成了路由的解析(Resolve)階段時(shí)觸發(fā)。
ChildActivationEnd 本事件會在路由器激活了路由的子路由時(shí)觸發(fā)。
ActivationEnd 本事件會在路由器激活了某個(gè)路由時(shí)觸發(fā)。
NavigationEnd 本事件會在導(dǎo)航成功結(jié)束之后觸發(fā)。
NavigationCancel 本事件會在導(dǎo)航被取消之后觸發(fā)。 這可能是因?yàn)樵趯?dǎo)航期間某個(gè)路由守衛(wèi)返回了 false。
NavigationError 這個(gè)事件會在導(dǎo)航由于意料之外的錯誤而失敗時(shí)觸發(fā)。
Scroll 本事件代表一個(gè)滾動事件。

當(dāng)啟用了 enableTracing 選項(xiàng)時(shí),這些事件也同時(shí)會記錄到控制臺中。要想查看對路由導(dǎo)航事件進(jìn)行過濾的例子,請?jiān)L問 Angular 中的可觀察對象一章的路由器部分

路由守衛(wè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è)配置過的路由器。 外殼組件中有一個(gè) RouterOutlet,它能顯示路由器所生成的視圖。 它還有一些 RouterLink,用戶可以點(diǎn)擊它們,來通過路由器進(jìn)行導(dǎo)航。

下面是一些路由器中的關(guān)鍵詞匯及其含義

路由器部件 含義
Router(路由器) 為激活的 URL 顯示應(yīng)用組件。管理從一個(gè)組件到另一個(gè)組件的導(dǎo)航。ts->this.router.navigateByUrl("/protel")
RouterModule 一個(gè)獨(dú)立的 Angular 模塊,用于提供所需的服務(wù)提供商,以及用來在應(yīng)用視圖之間進(jìn)行導(dǎo)航的指令。ts->RouterModule.forRoot(Routers數(shù)組,ExtraOptions對象)
Routes(路由數(shù)組) 定義了一個(gè)路由數(shù)組,每一個(gè)都會把一個(gè) URL 路徑映射到一個(gè)組件。ts->[(path:'',componet: ***)]
Route(路由) 定義路由器該如何根據(jù) URL 模式(pattern)來導(dǎo)航到組件。大多數(shù)路由都由路徑和組件類構(gòu)成。
RouterOutlet(路由出口) 該指令(<router-outlet>)用來標(biāo)記出路由器該在哪里顯示視圖。
RouterLink(路由鏈接) 這個(gè)指令把可點(diǎn)擊的 HTML 元素綁定到某個(gè)路由。點(diǎn)擊帶有 routerLink 指令(綁定到字符串或鏈接參數(shù)數(shù)組)的元素時(shí)就會觸發(fā)一次導(dǎo)航。html-><a [routerLink]="[./order]"></a>
RouterLinkActive(活動路由鏈接) 當(dāng) HTML 元素上或元素內(nèi)的routerLink變?yōu)榧せ罨蚍羌せ顮顟B(tài)時(shí),該指令為這個(gè) HTML 元素添加或移除 CSS 類。html中
ActivatedRoute(激活的路由) 為每個(gè)路由組件提供的一個(gè)服務(wù),它包含特定于路由的信息,比如路由參數(shù)、靜態(tài)數(shù)據(jù)、解析數(shù)據(jù)、全局查詢參數(shù)和全局碎片(fragment)。ts中
RouterState(路由器狀態(tài)) 路由器的當(dāng)前狀態(tài)包含了一棵由程序中激活的路由構(gòu)成的樹。它包含一些用于遍歷路由樹的快捷方法。
鏈接參數(shù)數(shù)組 這個(gè)數(shù)組會被路由器解釋成一個(gè)路由操作指南。你可以把一個(gè)RouterLink綁定到該數(shù)組,或者把它作為參數(shù)傳給Router.navigate方法。
路由組件 一個(gè)帶有RouterOutletAngular 組件,它根據(jù)路由器的導(dǎo)航來顯示相應(yīng)的視圖。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 摘要:在本教程中,Ahmed Bouchefra 介紹了angular路由器(router),以及如何使用它創(chuàng)建客...
    哈維爾23456閱讀 3,414評論 0 3
  • 官網(wǎng)鏈接: angular官網(wǎng) 路由與導(dǎo)航最好是跟著官網(wǎng)寫一遍代碼,然后來看這個(gè)總結(jié),會比較清晰 如何實(shí)現(xiàn)一個(gè)簡單...
    H_DaYan閱讀 3,303評論 0 6
  • 路由配置:路由配置是一個(gè)Routes類型的數(shù)組 例如: 創(chuàng)建根路由模塊: 通過調(diào)用RouterModule.for...
    我不傻_cyy閱讀 2,302評論 0 1
  • 一:路由基礎(chǔ) 什么是路由: 在web開發(fā)中,路由的概念由來已久,簡而言之,就是利用URL的唯一性來指定特定的事物,...
    真的稻城閱讀 6,068評論 2 7
  • 一、SPA 單頁Web應(yīng)用(single page web application,SPA),就是只有一張Web頁...
    笨蛋小明閱讀 948評論 0 3

友情鏈接更多精彩內(nèi)容