Angular從普通路由到惰性加載


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

四、結語

在做大型項目的時候,惰性加載無疑是比較好的選擇,各個模塊的路由各自對自己負責,有什么子組件就在自己的module.ts文件注冊,有什么路由規則就在各自的routing.module.ts文件里面定義,最后在整合代碼的時候,直接拷貝整個文件夾就可以了,非常方便。







免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM