本小节让我们共同完成前后台的对接。 在前面的章节中在进行班级列表组件对接的时候,我们直接在AppRouting中将klass路径指定到班级列表组件;并在App模块中引入了班级列表组件所在的模块来防止引用错误。本节将使用惰性加载的方法将班级列表组件与App模块进一步分离,从而达到惰性加载的目的(这可以大幅降低打包后项目的启动时间)。 ## 历史代码 在AppRoutingModule中,我们进行了如下声明: app-routing.module.ts ```js { path: 'klass', component: IndexComponent } @NgModule({ imports: [RouterModule.forRoot(routes)], ① exports: [RouterModule] ② }) export class AppRoutingModule { } ``` * ① 导入RouterModule,并使用routes对该模块进行路由配置。 * ② 导出配置过的RouterModule,其它模块若引用该模块(AppRoutingModule),则将自动引用RouterModule。 ![](https://img.kancloud.cn/f8/d2/f8d2735ad3016998f9b97644ef5a3dc7_935x408.png) 所以当我们在AppModule中有如下代码时: app.module.ts ``` @NgModule({ declarations: [ AppComponent, TeacherAddComponent, TeacherEditComponent, TeacherIndexComponent ], imports: [ BrowserModule, AppRoutingModule, ① HttpClientModule, FormsModule, KlassModule ② ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } ``` * ① 引入AppRoutingModule,同时引入了由AppRoutingModule配置过的路由模块RouterModule * ① 由于在路由模块中定义了`{path: 'klass', component: IndexComponent}`,所以: * ② 声明引入IndexComponent所在的KlassModule防止依赖错误 从思路上来讲,上述使用方法没有任何问题。但随着在路由中声明的组件越来越多,App模块在启动时将需要加载越来越多的信息来保证在路由中设定的component是可以被正确调用的,这将逐渐影响系统的启动速度。**惰性加载**便是解决此问题的最佳实践方法。 ## 惰性加载 app-routing.module.ts ``` { path: 'klass', component: IndexComponent ✘ loadChildren➊: () => import('./klass/klass.module')➋.then(mod➌ => mod.KlassModule➍) ✚ } ]; ``` * ➊ 当路径匹配`klass`时,加载子模块。 * ➋ 子模块位于`./klass/klass.module` * ➌ 子模块成功加载后,装入mod对象(./klass/klass.module.ts)。 * ➍ 返回mod对象中的KlassModule给➊ 然后测试时有点意思的事情就发生了: ![](https://img.kancloud.cn/72/ca/72cabef301a3649274dbc4ba12ddea89_920x409.gif) 通过观察我们发现,在启动首页的App模块时,控制台未报任何错误信息,而当我们点击`班级管理`时,控制台却开始报错了。 这是由于: * 在没有启用**惰性加载**前,系统启动时会尝试加载所有路由对应的组件 * 而在启用**惰性加载**后,App模块在获取到路由的loadChildren时,得到的是个`function`。当我们尝试访问`klass`路径时,angular才会执行这个`function`会,尝试得到程序下一步执行的模块KlassModule,并进入Klass模块继续执行。 我们再来观察一样修改前后的代码: app-routing.module.ts ``` { path: 'klass', component: IndexComponent ✘ loadChildren: () => import('./klass/klass.module').then(mod => mod.KlassModule) ✚ } ]; ``` 进行类型转换后: ``` { path: 'klass', component: 类 ---- angular立即尝试获取对应该类的对象 loadChildren: 函数 ---- angular立即将该函数载入(但不执行),仅当用户实际访问`klass`时才执行该函数。 } ]; ``` 由于`仅当用户实际访问`klass`时才执行该函数`,所以即使执行该函数以及在其以后的操作中会发生异常,也仅仅会在访问`klass`路径后发生。这也就是为什么当我们点击`班级管理`后,控制台才打印部分错误的原因。 ## 排错 让我们用最原始最有效的方法来解决下这个错误: ``` core.js:6014 ERROR Error: Uncaught (in promise): Error: BrowserModule has already been loaded. If you need access to common directives such as NgIf and NgFor from a lazy loaded module, import CommonModule instead. Error: BrowserModule has already been loaded. If you need access to common directives such as NgIf and NgFor from a lazy loaded module, import CommonModule instead. ``` 上述提示译成中文大概是说:`BrowserModule已经加载过了,如果你在惰性加载模块需要访问诸如NgIf或NgFor的指令,请使用CommonModule来替换BrowserModule`。 klass/klass.module.ts ``` @NgModule({ declarations: [IndexComponent, AddComponent], imports: [ BrowserModule, ✘ CommonModule, ✚ FormsModule, ReactiveFormsModule ] }) ``` #### 测试 此时,再次点击班级管理错误消息,但未显示任何信息: ![](https://img.kancloud.cn/9c/b2/9cb2e6961a38cd9c309df7df77a12380_542x372.png) 这是由于我们未在班级模块中定义任何路由的原因。 ## 定义路由 找到klass/klass.module.ts,增加路由配置后代码如下: ``` import {NgModule} from '@angular/core'; import {IndexComponent} from './index/index.component'; import {FormsModule, ReactiveFormsModule} from '@angular/forms'; import {AddComponent} from './add/add.component'; import {CommonModule} from '@angular/common'; import {RouterModule, Routes} from '@angular/router'; /*定义路由*/ const routes: Routes = [ ① { path: '', component: IndexComponent }, { path: 'add', component: AddComponent } ]; /** * 班级模块 */ @NgModule({ declarations: [IndexComponent, AddComponent], imports: [ CommonModule, FormsModule, ReactiveFormsModule, RouterModule.forChild(routes) ➊ ] }) export class KlassModule { } ``` * ① 定义路由 * ➊ 区别为forRoot,使用forChild定义惰性加载模块路由 在App模块中,我们使用的forRoot方法,这是由于App模块是我们系统的根模块,而forRoot即为:为根模块定义路由。虽然在惰性加载模块中使用了forChild来定义子路由,但并不必须这么做。在子模块中,也是可以使用forRoot来建立根路由的,但通常情况下我们并不这么做。 #### 测试 ![](https://img.kancloud.cn/57/9a/579a4f79e17743dcaed8be4abe91d0aa_468x296.png) # 参考文档 | 名称 | 链接 | 预计学习时长(分) | | --- | --- | --- | | 源码地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.3.6](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step3.3.6) | - | | 惰性加载路由配置 | [https://www.angular.cn/guide/router#lazy-loading-route-configuration](https://www.angular.cn/guide/router#lazy-loading-route-configuration) | 10 |