這篇文章我想來集中地講述一下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 { }
