NgModule 模块是Angular种一个重要的点,因为Angular的基本构造块就是NgModule。NgModule 会把相关的代码收集到一些功能集中,形成功能单元。在使用Angular CL 命令新建一个项目的时候,会给我们生成一个根模块,命名为 AppModule,根模块有一个根组件AppComponent,引导这个根模块就可以启动应用。Angular 应用是模块化的,我们在开发中会根据其功能 作用 以及其特性,建立大大小小各种模块,从而构建其成为一个应用程序,任何模块都能包含任意数量的其它组件。

@NgModule()

  1. import { BrowserModule } from '@angular/platform-browser';
  2. import { NgModule } from '@angular/core';
  3. import { AppRoutingModule } from './app-routing.module';
  4. import { AppComponent } from './app.component';
  5. @NgModule({
  6. declarations: [
  7. AppComponent
  8. ],
  9. imports: [
  10. BrowserModule,
  11. AppRoutingModule
  12. ],
  13. providers: [],
  14. bootstrap: [AppComponent]
  15. })
  16. export class AppModule { }

由⬆️代码我们可以看到,NgModule 是一个带有 @[NgModule](https://angular.cn/api/core/NgModule)() 装饰器的类,它接受一个元数据对象,该对象的属性用来描述这个模块。点进去@[NgModule](https://angular.cn/api/core/NgModule)() 装饰器的类我们可以看到他有如下属性以及官方的对其属性的解释。

  1. export declare interface NgModule {
  2. providers?: Provider[];// 本模块向全局服务中贡献的那些服务的创建器。 这些服务能被本应用中的任何部分使用。(你也可以在组件级别指定服务提供商,这通常是首选方式。)
  3. declarations?: Array<Type<any> | any[]>;// 那些属于本 NgModule 的组件、指令、管道
  4. imports?: Array<Type<any> | ModuleWithProviders<{}> | any[]>;// 那些导出了本模块中的组件模板所需的类的其它模块
  5. exports?: Array<Type<any> | any[]>;//那些能在其它模块的组件模板中使用的可声明对象的子集
  6. entryComponents?: Array<Type<any> | any[]>;
  7. bootstrap?: Array<Type<any> | any[]>;
  8. schemas?: Array<SchemaMetadata | any[]>;
  9. }

以下是本人使用Angular后对此元数据属性个人口语化的理解

  • providers:将本模块所有在组件中注入的服务,在这里提前定义好,否则在此模块中使用这个服务会有错误提示。
  • declaration:declaration 英文意思为声明。在这里声明一些模块中要使用到的一些组件,指令,管道等。
  • imports:导入一些模块,比如说我把所有的指令构成一个模块 我使用其中某些指令的时候,我可以选择导入整个指令模块。也可以导入一些通过npm install 安装的一些模块导入其中,才可以使用。
  • exports:导出组件or指令管道等,以供引用此模块的模块可以使用此模块的组件or 指令管道等。
  • exporyComponents:entry component 表示 angular 的入口组件,可以引导组件是一个入口组件,Angular 会在引导过程中把它加载到 DOM 中。 其它入口组件是在其它时机动态加载的。字面上的意义,但是啥时候用呢,比如,我要弹出一个组件,那么这个组件是要动态加载到DOM中了吧,这个时候就需要将这个组件xxxComponent写上了。
  • bootstrap:这个模块启动的时候应该启动的组件,上面代码可以看到AppModule是作为根模块的启动组件。
  • schemas:不属于Angular的组件或者指令的元素或者属性都需要在这里进行声明。

Javascript module VS Angular module

JavaScript 和 Angular 都使用模块来组织代码,虽然它们的组织形式不同,但 Angular 的应用会同时依赖两者。

JavaScript 模块

模块是内含 JavaScript 代码的独立文件。要让其中的东西可用,要写一个导出语句
例:

export class AppComponent { … }

在其他文件中需要使用

import { AppComponent } from ‘./app.component’;

<br />
而NgModulem模块我们在随笔的开头以及介绍他的元数据,对其有一定的了解了。
NgModule 类 与 JavaScript 模块有下列关键性的不同:

  1. NgModule 只绑定了可声明的类,这些可声明的类只是供 Angular 编译器用的。
  2. NgModule 与 JavaScript 类把它所有的成员类都放在一个巨型文件中不同,只要把该模块的类列在它的 @NgModule.declarations 列表中。
  3. NgModule 只能导出可声明的类。这可能是它自己拥有的也可能是从其它模块中导入的。它不会声明或导出任何其它类型的类。
  4. 与 JavaScript 模块不同,NgModule 可以通过把服务提供商加到 @NgModule.providers 列表中,来用服务扩展整个应用。

相比之下我们可以看出,NgModulem模块更灵活,扩展性强,更具优势。

常用模块

首先要知道跑起来一个项目需要引用什么基本的模块,以下是Angular 提供的一些官方的模块。

NgModule 导入自 为何使用
[BrowserModule](https://angular.cn/api/platform-browser/BrowserModule) @angular/platform-browser 当你想要在浏览器中运行应用时
[CommonModule](https://angular.cn/api/common/CommonModule) @angular/common 当你想要使用 [NgIf](https://angular.cn/api/common/NgIf)NgFor
[FormsModule](https://angular.cn/api/forms/FormsModule) @angular/forms 当要构建模板驱动表单时(它包含 [NgModel](https://angular.cn/api/forms/NgModel)
[ReactiveFormsModule](https://angular.cn/api/forms/ReactiveFormsModule) @angular/forms 当要构建响应式表单时
[RouterModule](https://angular.cn/api/router/RouterModule) @angular/router 要使用路由功能,并且你要用到 [RouterLink](https://angular.cn/api/router/RouterLink),.forRoot().forChild()
[HttpClientModule](https://angular.cn/api/common/http/HttpClientModule) @angular/common/[http](https://angular.cn/api/common/http) 当你要和服务器对话时

特性模块分类

官方文档将模块分为五大类。

  • 领域特性模块
  • 带路由的特性模块
  • 路由模块
  • 服务特性模块
  • 可视部件特性模块

虽然我特么当年根本不知道,但是在开发中慢慢摸索其实发现也是根据模块的特性将模块的分类,结果不经相同。
以下为个人在开发中对功能模块的划分
1.业务型模块:整一个应用程序,根据其业务功能我们可以将程序拆分为一个个模块,有很明确的业务特性,围绕其业务功能的模块。例如:用户模块,订单模块等。它有自己独立的路由,有提供与此模块的服务,有一个or多个组件,它惰性懒加载,不会导出or提供任何组件or指令管道,引用官方、本应用程序or第三方的功能模块。它有明确的业务特性,不与别的模块有耦合性。
2.组件模块:应用程序中通常都有规范化的标准设计 ,比如说统一的table,card date 等。将这些都抽出来,做成一个个组件,在模块中导出此组件以供其他模块使用,这样减少了应用程序中重复的样式代码等。曾经我是将所有这种可能多处要使用的封装为组件后,统一在一个模块中导出,后来演变为每一个组件都拆分为一个模块。这样也是发现如果这种通用性的组件多起来的话,假设有二三十个组件在这个UIComponent模块中,而我因为要使用其中一两个组件而导入这个模块,性能是很差的,所以后来都将组件拆分为一个个模块以供业务模块使用,例:DateModule,InputModule..等。
3.服务模块:提供一些通用型的服务。比如说http服务对httpClient二次包装适用于项目,文件服务,配置服务等。

4.其他模块:应用程序中我们会根据需要会做一些指令管道等,其就形成一个指令模块包含应用程序中所有等指令,管道模块包含应用程序中的所有管道。后来觉得,其实这些指令管道不需要集中起来统一导出引用。因为一个模块并不会引用到指令模块中超过百分之八十的指令,so 只需要把它们集中到一个pipe文件夹下,哪个模块需要用到具体个指令or管道,直接声明在其模块中使用便可。

创建,导入特性模块

我们将系统根据其功能 业务划分好模块,有利于合作开发,代码的维护和使用。

创建特性模块

**

  1. ng g m order // 创建订单模块
  2. // ng g m order --routing //创建的订单模块带路由
  3. ng g c order/list // 订单模块下新建一个list 组件

我们看最后cli给我们生成的目录结构

ngModule - 图1

order.module.ts

  1. import { NgModule } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { ListComponent } from './list/list.component';
  4. @NgModule({
  5. declarations: [ListComponent],//定义list组件
  6. exports: [ListComponent],//导出list组件
  7. imports: [
  8. CommonModule
  9. ]
  10. })
  11. export class OrderModule { }

list.component.ts

  1. import { Component, OnInit } from '@angular/core';
  2. @Component({
  3. selector: 'app-list',
  4. templateUrl: './list.component.html',
  5. styleUrls: ['./list.component.scss']
  6. })
  7. export class ListComponent implements OnInit {
  8. constructor() { }
  9. ngOnInit() {
  10. }
  11. }

导入使用特性模块

现在我们导入根模块
app.module.ts

  1. import { BrowserModule } from '@angular/platform-browser';
  2. import { NgModule } from '@angular/core';
  3. import { AppRoutingModule } from './app-routing.module';
  4. import { AppComponent } from './app.component';
  5. import { OrderModule } from './order/order.module';
  6. @NgModule({
  7. declarations: [
  8. AppComponent
  9. ],
  10. imports: [
  11. BrowserModule,
  12. AppRoutingModule,
  13. OrderModule //将order模块导入
  14. ],
  15. providers: [],
  16. bootstrap: [AppComponent]
  17. })
  18. export class AppModule { }

**
app.component.html 在跟模块使用

  1. <!--The content below is only a placeholder and can be replaced.-->
  2. <div style="text-align:center">
  3. <h1>
  4. Welcome to {{ title }}!
  5. </h1>
  6. </div>
  7. <app-list></app-list>
  8. <router-outlet></router-outlet>

我们可以看到渲染了order模块的list组件

ngModule - 图2

惰性加载模块

如果我们将所有的模块都导入根模块,那么应用在初始化加载的时候就会非常慢。这时候我们应该考虑使用惰性加载。根据需求加载相应都模块,减少应用初始化包的大小以及减少加载的时间,提高用户体验性。
惰性加载的模块特点是该模块拥有路由模块。so 接着上面我们创建了一个订单模块 我们给订单模块加上路由。并再创建一个user.module以及user.module模块下的list组件。

ngModule - 图3

order.module

  1. import { NgModule } from '@angular/core';
  2. import { CommonModule } from '@angular/common';
  3. import { OrderRoutingModule } from './order-routing.module';
  4. import { ListComponent } from './list/list.component';
  5. @NgModule({
  6. declarations: [ListComponent],
  7. imports: [
  8. CommonModule,
  9. OrderRoutingModule
  10. ]
  11. })
  12. export class OrderModule { }

order-routing.module

  1. import { NgModule } from '@angular/core';
  2. import { Routes, RouterModule } from '@angular/router';
  3. import { ListComponent } from './list/list.component';
  4. const routes: Routes = [
  5. {
  6. path: 'list',
  7. component: ListComponent
  8. },
  9. ];
  10. @NgModule({
  11. imports: [RouterModule.forChild(routes)],
  12. exports: [RouterModule]
  13. })
  14. export class OrderRoutingModule { }

user模块如此类推
接下来配置路由
AppRoutingModule在顶级路由中配置

  1. import { NgModule } from '@angular/core';
  2. import { Routes, RouterModule } from '@angular/router';
  3. const routes: Routes = [
  4. {
  5. path: 'orders',
  6. loadChildren: './order/order.module#OrderModule'
  7. },
  8. {
  9. path: 'orders',
  10. loadChildren: './user/user.module#UserModule'
  11. }
  12. ];
  13. @NgModule({
  14. imports: [RouterModule.forRoot(routes)],
  15. exports: [RouterModule]
  16. })
  17. export class AppRoutingModule { }

我们给app.component.html新增两个button

  1. <!--The content below is only a placeholder and can be replaced.-->
  2. <div style="text-align:center">
  3. <h2>
  4. Welcome to {{ title }}!
  5. </h2>
  6. </div>
  7. <button routerLink="/user/list">user</button>
  8. <button routerLink="/order/list">order</button>
  9. <router-outlet></router-outlet>

效果图

ngModule - 图4
惰性加载模块有什么好处呢,在大型项目中往往有许多个模块,而且大很大。如果一个模块1m,如果我们在浏览器输入地址打开这个应用,瞬间要加载100m 是非常慢的,而且我们并非要是用到着这100个模块。将系统业务拆分为各个模块,划分好界限。按需加载,我点击了user 我加载user 模块我出现user 列表,对user进行操作。当我需要使用时才加载极大的减少了页面初始加载的时间以及减少了资源的消耗。

ngModule - 图5

共享模块

共享模块顾名思义,就是共享于所有的模块中。首先得定义好这个模块的具体功能特性,比如指令、管道和组件等分别封装成一个个模块,哪些业务模块需要使用到其里面的功能变导入其模块中便可。简单的比如,本系统的input 都是统一样式的,我们可以制作一个input 模块 然后在其他模块直接导入使用。这极大的规范了系统的统一性和降低了以后的维护成本。

ngModule - 图6