你的位置:首页 > Java教程

[Java教程]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';//从路由库导入RouterModuleimport { 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 Codeapp.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 Codepagenot.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,Routesimport { 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 Codeapp-routing.module.ts
import { NgModule }    from '@angular/core';//从路由库导入RouterModule,Routesimport { 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';//导入路由定义文件AppRoutingModuleimport { AppRoutingModule } from './app-routing.module';//以下是组件的导入import { AppComponent } from './app.component';import { PageNotComponent } from './pagenot';import { SidebarComponent } from './side-bar/sidebar.component';//模块的导入import { HomeModule } from './home/home.module';import { DetailModule } from './detail/detail.module';import { ContactModule } from './contact/contact.module';import { HomeRoutingModule } from './home/home-routing.module';import { DetailRoutingModule } from './detail/detail-routing.module';import { ContactRoutingModule } from './contact/contact-routing.module';@NgModule({ declarations: [ AppComponent, PageNotComponent, SidebarComponent ], imports: [ BrowserModule, AppRoutingModule, HomeRoutingModule, DetailRoutingModule, ContactRoutingModule, HomeModule, DetailModule, ContactModule ], providers: [], bootstrap: [AppComponent]})export class AppModule { }
View Code我们可以发现,不用导入首页组件等组件了,而且这些父组件的子组件也不用导入了,当我们的项目很庞大的时候,一个父组件可能连一个表格都能分成一个子组件,用这种惰性加载的方式,极大地把项目模块化管理。在这个demo中,比如首页组件,首页组件里面假如有很多子组件,那么我们要使用这些子组件的时候,肯定要在module.ts文件里面导入并且注册,有了惰性加载,我们就不用每个组件都在app.module.ts文件里面注册,而是在各自对应的父组件的module.ts文件比如home.module.ts里面注册,然后再把home.module.ts暴露出来的HomeModule导入app.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: 'app',    component: AppComponent,    children: [     {      path: '',      children: [       { path: '**', component: PageNotComponent }      ]     },     {      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     }    ]   }  ]) ], 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 Codehome-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: '',    children: [     { path: 'home', component: HomeComponent,      children:[{ path: 'child', component:ChildrenComponent}]      }    ]   }  ]) ], exports: [  RouterModule ]})export class HomeRoutingModule { }
View Codedetail.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 Codedetail-routing.module.ts
import { NgModule } from '@angular/core';import { RouterModule } from '@angular/router';import { DetailComponent } from './detail.component';@NgModule({ imports: [  RouterModule.forChild([   {    path: '',    children: [     { path: 'detail', component: DetailComponent }    ]   }  ]) ], exports: [  RouterModule ]})export class DetailRoutingModule { }
View Codecontact.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 Codecontact-routing.module.ts
import { NgModule } from '@angular/core';import { RouterModule } from '@angular/router';import { ContactComponent } from './contact.component';@NgModule({ imports: [  RouterModule.forChild([   {    path: '',    children: [     { path: 'contact', component: ContactComponent }    ]   }  ]) ], exports: [  RouterModule ]})export class ContactRoutingModule { }
View Code

四、结语

在做大型项目的时候,惰性加载无疑是比较好的选择,各个模块的路由各自对自己负责,有什么子组件就在自己的module.ts文件注册,有什么路由规则就在各自的routing.module.ts文件里面定义,最后在整合代码的时候,直接拷贝整个文件夹就可以了,非常方便。