這篇文章我想來集中地講述一下Angular路由的普通應用到惰性加載的知識,對我這段時間的學習做一個全面的匯總!
Angular的路由,我把它的演變過程分成三個階段:
1.Angular路由直接在app.module.ts-->imports--> RouterModule里面編寫路由;
2.由於直接在 RouterModule里面編寫路由不方便路由管理,會使得imports里面的內容過於冗長,所以在app-routing.module.ts里面,把路由的編寫代碼獨立出來;
3.當我們在做項目的時候,一個項目往往由不止一個人負責,這時候用第二種方法我們會發現,當我們最后要把每個人負責的部分整合起來的時候,要在app.module.ts里面導入一大堆的組件,注冊一大堆的組件,這樣使得代碼編寫混亂,從而不利於其他程序員閱讀和后期的代碼修改。因此,就出現了惰性加載這種東西。
接下來我們分三個部分來解說一下路由的成長歷程。(PS:下面所有demo的css樣式不是講述重點,這里省略)
一、Angular路由的嬰兒時期
嬰兒時期指的是Angular路由直接在app.module.ts-->imports--> RouterModule里面編寫路由,我們用一個例子來看一下嬰兒時期的路由。
1.demo效果

圖里面的表格可以看出我們接下來這個demo的路由規則
2.demo目錄
----------app.comonent.ts
----------app.component.html
----------pagenot.ts
----------pagenot.html(地址錯誤顯示界面)
----------app.module.ts
----------home(文件夾)
------------home.component.ts
------------home.component.html
------------children(子路由文件夾)
--------------children.component.ts
--------------children.component.html
----------detail(文件夾)
------------detail.component.ts
------------detail.component.html
----------contact(文件夾)
------------contact.component.ts
------------contact.component.html
----------side-bar(文件夾)
------------sidebar.component.ts
------------sidebar.component.html
3.代碼講解
看到上面的目錄,其實才幾個組件,注冊就要一大堆,慢慢的我們就會發現惰性加載的重要性,這個是后話了。
app.module.ts

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; //從路由庫導入RouterModule import { RouterModule } from '@angular/router'; //以下是組件的導入 import { AppComponent } from './app.component'; import { HomeComponent } from './home/home.component'; import { DetailComponent } from './detail/detail.component'; import { ContactComponent } from './contact/contact.component'; import { SidebarComponent } from './side-bar/sidebar.component'; import { ChildrenComponent } from './home/children/children.component'; import { PageNotComponent } from './pagenot'; @NgModule({ declarations: [ AppComponent, HomeComponent, DetailComponent, ContactComponent, SidebarComponent, ChildrenComponent, PageNotComponent ], imports: [ BrowserModule, //定義路由規則 RouterModule.forRoot([ { path: 'home', component: HomeComponent,//首頁組件 children:[{ path: 'child', component: ChildrenComponent //首頁的子路由組件 }] }, { path: 'detail', component: DetailComponent //詳情頁組件 }, { path: 'contact', component: ContactComponent //聯系方式組件 }, { //重定向路由 path: '', redirectTo: '/home', //當url為空的時候,跳轉到HomeComponent組件 pathMatch: 'full' }, { //通配符路由 path: '**', component: PageNotComponent //頁面錯誤組件 } ]) ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
在這里其實要注意的是
重定向路由一定要在通配符路由之前,否則在整個URL等於''時,如果通配符路由在重定向路由之前,通配符路由會以為是地址錯誤從而顯示的是地址錯誤界面。在我們這個例子中默認路由應該只有在整個URL等於''時才重定向到HomeComponent,別忘了把重定向路由設置為pathMatch = 'full'。
app.component.html

<div class="jumbotron"> <h1>Welcome to use Angular</h1> <p>...</p> </div> <div class="contain-wrapper"> <sidebar></sidebar> <div class="result-wrapper"> <router-outlet></router-outlet> </div> </div>
app.component.ts(其他的ts文件跟這個文件差不多,只是修改相對應的名字而已,每個名字對應的組件我在app.module.ts標注)

import { Component } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'app'; }
pagenot.html
<div class="pagenot"> <p>地址錯誤請重新輸入!</p> </div>
home.component.html
<p>這是首頁</p> <div class="home-contain"> <button routerLink="/home/child" class="btn btn-primary">點擊這里運用子路由</button> <div class="children-contain"> <router-outlet></router-outlet> </div> </div>
children.component.html
<table class="table table-hover"> <caption>路由</caption> <tr> <th></th> <th>首頁</th> <th>詳情</th> <th>聯系方式</th> </tr> <tr> <td>有無子路由</td> <td>有</td> <td>無</td> <td>無</td> </tr> </table>
detail.component.html
<p>這是詳情頁</p>
contact.component.html
<p>這是聯系方式頁面</p>
sidebar.component.html
<div class="sidebar"> <ul> <li><a routerLink="/home">首頁</a></li> <li><a routerLink="/detail">詳情</a></li> <li><a routerLink="/contact">聯系方式</a></li> </ul> </div>
4.總結
我們來捋一下路由使用的步驟
(1)在app.module.ts里面導入所有組件並注冊,然后導入RouterModule,並且在imports里面注冊,同時定義路由規則;
(2)在相應的鏈接用routerLink="/路由"定義鏈接要跳轉的路由;
(3)在需要的位置設置路由出口<router-outlet></router-outlet>,一個模板中只能有一個未命名的<router-outlet>。
5.小小的改進,逐漸向少年期過渡
直接在imports里面直接定義路由規則有些不符合我們的變成習慣,我們可以將這部分獨立出來,這樣才使得每個模塊功能明確,直接修改app.module.ts文件就可以了
import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; //從路由庫導入RouterModule,Routes import { RouterModule,Routes } from '@angular/router'; //以下是組件的導入 import { AppComponent } from './app.component'; import { HomeComponent } from './home/home.component'; import { DetailComponent } from './detail/detail.component'; import { ContactComponent } from './contact/contact.component'; import { SidebarComponent } from './side-bar/sidebar.component'; import { ChildrenComponent } from './home/children/children.component'; import { PageNotComponent } from './pagenot'; //定義路由規則,把這塊獨立出來 const appRoutes: Routes = [ { path: 'home',component: HomeComponent, //首頁組件 children:[{path: 'child',component: ChildrenComponent}]}, //首頁子路由組件 { path: 'detail',component: DetailComponent}, //詳情頁組件 { path: 'contact',component: ContactComponent}, //聯系方式組件 { path: '',redirectTo: '/home',pathMatch: 'full'}, //重定向組件 { path: '**',component: PageNotComponent} //通配符路由組件 ]; @NgModule({ declarations: [ AppComponent, HomeComponent, DetailComponent, ContactComponent, SidebarComponent, ChildrenComponent, PageNotComponent ], imports: [ BrowserModule, RouterModule.forRoot(appRoutes) ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
但是說實話,這樣的分離還不夠徹底,路由該長大了。
二、Angular路由的少年時期
為了分工明確,后期容易管理,我們直接把路由規則用一個叫做app-routing.module.ts的文件把它獨立出來
在app.module.ts同級目錄下添加app-routing.module.ts文件
app.module.ts修改

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; //導入AppRoutingModule路由定義文件 import { AppRoutingModule } from './app-routing.module'; //以下是組件的導入 import { AppComponent } from './app.component'; import { HomeComponent } from './home/home.component'; import { DetailComponent } from './detail/detail.component'; import { ContactComponent } from './contact/contact.component'; import { SidebarComponent } from './side-bar/sidebar.component'; import { ChildrenComponent } from './home/children/children.component'; import { PageNotComponent } from './pagenot'; @NgModule({ declarations: [ AppComponent, HomeComponent, DetailComponent, ContactComponent, SidebarComponent, ChildrenComponent, PageNotComponent ], imports: [ BrowserModule, AppRoutingModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
app-routing.module.ts

import { NgModule } from '@angular/core'; //從路由庫導入RouterModule,Routes import { RouterModule,Routes } from '@angular/router'; //以下是組件的導入 import { AppComponent } from './app.component'; import { HomeComponent } from './home/home.component'; import { DetailComponent } from './detail/detail.component'; import { ContactComponent } from './contact/contact.component'; import { SidebarComponent } from './side-bar/sidebar.component'; import { ChildrenComponent } from './home/children/children.component'; import { PageNotComponent } from './pagenot'; //定義路由規則,把這塊獨立出來 const appRoutes: Routes = [ { path: 'home',component: HomeComponent, //首頁組件 children:[{path: 'child',component: ChildrenComponent}]}, //首頁子路由組件 { path: 'detail',component: DetailComponent}, //詳情頁組件 { path: 'contact',component: ContactComponent}, //聯系方式組件 { path: '',redirectTo: '/home',pathMatch: 'full'}, //重定向組件 { path: '**',component: PageNotComponent} //通配符路由組件 ]; @NgModule({ imports: [ RouterModule.forRoot(appRoutes) ], exports: [ RouterModule ] }) export class AppRoutingModule {}
三、Angular路由的成熟期(惰性加載)
1.demo目錄
----------app.comonent.ts
----------app.component.html
----------pagenot.ts
----------pagenot.html(地址錯誤顯示界面)
----------app.module.ts
----------app-routing.module.ts
----------home(文件夾)
------------home.module.ts
------------home-routing.module.ts
------------home.component.ts
------------home.component.html
------------children(子路由文件夾)
--------------children.component.ts
--------------children.component.html
----------detail(文件夾)
------------detail.module.ts
------------detail-routing.module.ts
------------detail.component.ts
------------detail.component.html
----------contact(文件夾)
------------contact.module.ts
------------contact-routing.module.ts
------------contact.component.ts
------------contact.component.html
----------side-bar(文件夾)
------------sidebar.component.ts
------------sidebar.component.html
下面代碼解析只解析有更改的部分
2.代碼講解
app.module.ts

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; //導入路由定義文件AppRoutingModule import { AppRoutingModule } from './app-routing.module'; //以下是組件的導入 import { AppComponent } from './app.component'; import { PageNotComponent } from './pagenot'; import { SidebarComponent } from './side-bar/sidebar.component'; @NgModule({ declarations: [ AppComponent, PageNotComponent, SidebarComponent ], imports: [ BrowserModule, AppRoutingModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
我們可以發現,不用導入首頁組件等組件了,而且這些父組件的子組件也不用導入了,當我們的項目很龐大的時候,一個父組件可能連一個表格都能分成一個子組件,用這種惰性加載的方式,極大地把項目模塊化管理。在這個demo中,比如首頁組件,首頁組件里面假如有很多子組件,那么我們要使用這些子組件的時候,肯定要在module.ts文件里面導入並且注冊,有了惰性加載,我們就不用每個組件都在app.module.ts文件里面注冊,而是在各自對應的父組件的module.ts文件比如home.module.ts里面注冊。
app-routing.module.ts

import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { PageNotComponent } from './pagenot'; import { AppComponent } from './app.component'; @NgModule({ imports: [ RouterModule.forRoot([ { path: '', redirectTo: '/home', pathMatch: 'full'}, { path: 'home', loadChildren: 'app/home/home.module#HomeModule' //Lazy load home module }, { path: 'detail', loadChildren: 'app/detail/detail.module#DetailModule' //Lazy load detail module }, { path: 'contact', loadChildren: 'app/contact/contact.module#ContactModule' //Lazy load contact module }, { path: '**', component: PageNotComponent } ]) ], exports: [RouterModule] }) export class AppRoutingModule { }
當我們把RouterModule.forRoot改成RouterModule.forChild的時候,會出現這個錯誤

該錯誤是由於RouterModule.forChild()生成一個包含必要指令和路由但不包含路由服務的模塊而引起的。而RouterModule.forRoot(),它生成一個包含必要指令,路由和路由服務的模塊。
home.module.ts

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { HomeRoutingModule } from './home-routing.module'; //以下是組件的導入 import { HomeComponent } from './home.component'; import { ChildrenComponent } from './children/children.component'; @NgModule({ declarations: [ HomeComponent, ChildrenComponent ], imports: [ BrowserModule, HomeRoutingModule ] }) export class HomeModule { }

import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { HomeComponent } from './home.component'; import { ChildrenComponent } from './children/children.component'; @NgModule({ imports: [ RouterModule.forChild([ { path: '', component: HomeComponent, children:[{ path: 'child', component:ChildrenComponent}] } ]) ], exports: [ RouterModule ] }) export class HomeRoutingModule { }
detail.module.ts

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { DetailRoutingModule } from './detail-routing.module'; //以下是組件的導入 import { DetailComponent } from './detail.component'; @NgModule({ declarations: [ DetailComponent ], imports: [ BrowserModule, DetailRoutingModule ] }) export class DetailModule { }
detail-routing.module.ts

import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { DetailComponent } from './detail.component'; @NgModule({ imports: [ RouterModule.forChild([ { path: '', component: DetailComponent } ]) ], exports: [ RouterModule ] }) export class DetailRoutingModule { }
contact.module.ts

import { BrowserModule } from '@angular/platform-browser'; import { NgModule } from '@angular/core'; import { ContactRoutingModule } from './contact-routing.module'; //以下是組件的導入 import { ContactComponent } from './contact.component'; @NgModule({ declarations: [ ContactComponent ], imports: [ BrowserModule, ContactRoutingModule ] }) export class ContactModule { }
contact-routing.module.ts

import { NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { ContactComponent } from './contact.component'; @NgModule({ imports: [ RouterModule.forChild([ { path: '', component: ContactComponent } ]) ], exports: [ RouterModule ] }) export class ContactRoutingModule { }