这篇文章我想来集中地讲述一下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 { }