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 { }
在这里其实要注意的是 重定向路由一定要在通配符路由之前,否则在整个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 {}
现在看起来舒服多了,每个版块各司其职。但是大家有没有想过,如果每个主要的组件比如首页组件,详情页组件,联系方式组件里面还有很多的子组件的话,那么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 { }
我们可以发现,不用导入首页组件等组件了,而且这些父组件的子组件也不用导入了,当我们的项目很庞大的时候,一个父组件可能连一个表格都能分成一个子组件,用这种惰性加载的方式,极大地把项目模块化管理。在这个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 { }
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 { }
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 { }

四、结语

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







猜你喜欢

转载自blog.csdn.net/qq_34551390/article/details/78528051