這篇文章我想來集中地講述一下Angular路由的普通應用到惰性載入的知識,對我這段時間的學習做一個全面的彙總! Angular的路由,我把它的演變過程分成三個階段: 1.Angular路由直接在app.module.ts-->imports--> RouterModule裡面編寫路由; 2.由於直接 ...
這篇文章我想來集中地講述一下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.html3.代碼講解
看到上面的目錄,其實才幾個組件,註冊就要一大堆,慢慢的我們就會發現惰性載入的重要性,這個是後話了。 app.module.tsimport { 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 { }View Code 在這裡其實要註意的是重定向路由一定要在通配符路由之前,否則在整個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>View Code 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'; }View Code 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 { }View Code 但是說實話,這樣的分離還不夠徹底,路由該長大了。
二、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 { }View Code 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 {}View Code 現在看起來舒服多了,每個版塊各司其職。但是大家有沒有想過,如果每個主要的組件比如首頁組件,詳情頁組件,聯繫方式組件裡面還有很多的子組件的話,那麼module.ts文件就會像懶婆娘的裹腳布一樣,又長又臭。
三、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.tsimport { 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 { }View Code 我們可以發現,不用導入首頁組件等組件了,而且這些父組件的子組件也不用導入了,當我們的項目很龐大的時候,一個父組件可能連一個表格都能分成一個子組件,用這種惰性載入的方式,極大地把項目模塊化管理。在這個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 { }View Code 當我們把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 { }View Code home-routing.module.ts
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 { }View Code 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 { }View Code 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 { }View Code 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 { }View Code 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 { }View Code