NgModule 是你使用 Angular 编写应用程序时遇到的第一个基本结构,但由于涉及不同的作用域范围,它也是最微妙和最复杂的。如果你想详细了解 NgModule 的相关知识,可以直接参考 Angualr NgModule FAQ 或 Angular FAQ 文章中 NgModule 版块的内容 。

Why NgModule?

我们可以使用 Angular CLI,自动完成很多工作,但我们必须要做的第一件事就是加载根模块。

platformBrowserDynamic().bootstrapModule(AppModule);

NgModule 的目的是声明我们在 Angular 模块中创建的内容,主要有以下两种结构:

  • declarations - 声明模板中使用的内容,大部分是组件,也包括指令和管道。

  • providers - 用于声明服务。

import { NgModule } from '@angular/core';import { SomeComponent } from './some.component';
import { SomeDirective } from './some.directive';
import { SomePipe } from './some.pipe';
import { SomeService } from './shared/some.service';@NgModule({declarations: [SomeComponent, SomeDirective, SomePipe],providers: [SomeService]
})
export class SomeModule {}

NgModule 是 Angular RC 阶段新增的一个功能,因为 ES 6 中已经新增 import/export 功能,在引入 NgModule 似乎增加了不必要的复杂性。但引入了 NgModule 让我们可以使用 AOT 编译,这大大提高了应用的性能。在 Angular 2 早期 Beta 版本中,我们的组件在使用指令时,都必须每次导入相应的指令,这大大增加开发成本。

NgModule and scopes/visibility

declarationsproviders 属性最令人困惑的是,它们没有相同的作用域和可见性 (scope / visibility):

  • declarations / components 是本地作用域 (private visibility)

  • providers / services 是全局作用域 (public visibility)

这意味着你声明的组件只能在当前模块中使用。如果你想要在外面使用声明的组件,你必须导出它们:

import { NgModule } from '@angular/core';import { SomeComponent } from './some.component';
import { SomeDirective } from './some.directive';
import { SomePipe } from './some.pipe';@NgModule({declarations: [SomeComponent, SomeDirective, SomePipe],exports: [SomeComponent, SomeDirective, SomePipe]
})
export class SomeModule {}

反之,模块中的声明的服务,我们可以在所有模块中使用。

When to import a NgModule?

components 和 services 拥有不同的作用域,了解这个区别很重要。如果我们的应用程序不仅仅包含一个模块,事情可能会变得很糟糕。Angular 框架内部也拆分成多个不同的模块,如 core、common、http 等等。

在 Angular 模块中我们需要做的一件主要的事情是导入其它模块:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpModule } from '@angular/http';import { FeatureModule } from '../feature/feature.module';@NgModule({imports: [CommonModule, HttpModule, FeatureModule]
})
export class SomeModule {}

现在的主要问题是,你必须知道为什么需要导入这些模块:

  • 为了使用导入模块中声明的组件、指令或管道?

  • 为了使用模块中定义的服务?

因为组件和服务,拥有不同的作用域:

  • 如果我们需要在模块中使用导入模块中声明的组件,那我们需要在每个使用的模块中导入对应的模块

  • 如果我们只是使用模块中定义的服务,那我们只需要在主模块中导入对应的模块

如果你不了解这些区别,你可能由于忘记导入某个模块,而出现组件不可用的错误。或者你为了使用某个模块中定义的服务,而多次导入同一个模块。

When to import main Angular Modules?

Modules to import each time you need them

  • CommonModule (包含 Angular 中定义的内建指令,如 ngIf、ngFor 等),除了在主模块之外,不需要导入,因为我们已经在主模块中导入了 BrowserModule (此模块已导入了 CommonModule)。其它模块都必须手动导入该模块。

  • FormsModule / ReactiveFormsModule

  • BrowserAnimationsModule

  • FlexLayoutModule

  • MaterialModule 和 UI Modules (如 PrimeNg)

  • Other Modules (定义 components、directives 或 pipes)

Modules to import only once

  • HttpModule

  • Other Modules (仅提供服务)

The ShareModule good practice

若使用 Angular CLI 创建新的模块,它会自动帮我们导入 CommonModule。如果你需要使用与组件相关的模块如 animations 、flex layout 或 Material 模块,你必须每次手动导入上述模块。因此一个好的实践是创建 SharedModule 模块。

管理 SharedModule 时,需要很小心。如果你只是导入模块,它将无法正常工作:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MaterialModule } from '@angular/material';@NgModule({imports: [CommonModule, FlexLayoutModule, MaterialModule]
})
export class SharedModule {}

为什么呢?原因仍是作用域的问题:组件只在 SharedModule 中可用,在其它导入 SharedModule 的模块中,仍然是不可用的。因此我们需要导出相应的组件:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MaterialModule } from '@angular/material';@NgModule({imports: [CommonModule, FlexLayoutModule, MaterialModule],exports: [CommonModule, FlexLayoutModule, MaterialModule]
})
export class SharedModule {}

如果我们组件不包含其它共享的资源,我们可以省略 imports 属性:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FlexLayoutModule } from '@angular/flex-layout';
import { MaterialModule } from '@angular/material';@NgModule({exports: [CommonModule, FlexLayoutModule, MaterialModule]
})
export class SharedModule {}

Mixed NgModule

如何同时管理具有组件和服务的模块?这是一个比较复杂的问题。你可能已经接触过 RouterModule,该模块不仅提供了 <router-outlet>routerLink 指令,而且还提供了 ActivedRouter 服务 (用于获取 URL 参数)、Router 服务 (用于页面导航) 。

幸运的是,这个问题是由模块本身来解决。 Angular CLI 会为我们自动生成路由文件,但你可能已经注意到,应用程序主模块的路由和子模块路由之间存在细微差别。

对于 AppModule,我们这样使用:

RouterModule.forRoot(routes)

对于子模块,我们这样使用:

RouterModule.forChild(routes)

为什么呢?因为在 AppModule 中,forRoot() 方法会导入路由模块中的指令和服务。但对于子模块来说,forChild() 方法仅会导入路由模块中定义的指令,而不会再次导入模块中定义的服务。

Lazy-loaded modules

如果你需要实现模块懒加载,可以简单地使用 Angular CLI 来实现:

const routes: Routes = [{ path: 'admin', loadChildren: './admin/admin.module#AdminModule' }
];

因为它将是一个不同的包和模块,默认情况下是按需加载,它不会包含在您的应用程序的全局范围内。

对于组件来说,你不需要更改任何内容:就像在任何子模块中一样,你需要再次导入 CommonModule 或 SharedModule 模块。

但对于服务来说,有一些区别:

  • 你仍然可以访问应用程序中提供的服务 (如Http和已定义的服务)。

  • 延迟加载模块中定义的服务,只能在延迟加载的模块中使用,应用程序的其它模块是无法使用的。

参考资源

  • Understanding Angular modules (NgModule) and their scopes

Angular NgModule 作用域相关问题相关推荐

  1. Angular讲义 ---作用域

    什么是作用域? Angular中作用域(scope)是模板以及工作的上下文环境,作用域中存放了应用模型和视图相关的回调行为.作用域是层次化结构的与相关联的DOM结构相对应.作用域可以观察表达式以及传播 ...

  2. JS作用域相关知识(#精)

    在学习<你不知道的JS>一书中,特将作用域相关知识在此分享一下: #说到作用域,就不得不提到LHS查询和RHS查询: 1)如果查询目的是对变量进行赋值,则使用LHS查询 2)如果查询目的是 ...

  3. Angular NgModule 中的 declarations 和 exports定义

    我在app.component.ts的template文件里试图使用另一个Component时,遇到如下错误消息: app-parent-child is not a known element: 在 ...

  4. Angular NgModule providers字段维护了多个字段后的初始化实现

    下图是Angular依赖注入运行时处理的multi records数据结构: 对应我在NgModule的providers区域里定义的providers record: 在一个for循环里依次按顺序注 ...

  5. angular ng lint 相关

    首先还是要来讲一讲 ng lint 的相关知识: 通过Angular CLI的执行语句 **ng new XXX** 创建新项目后,目录中会包含一个tslint.json文件,这个文件就是用来定义一个 ...

  6. Angular NgModule

    NgModule 1,定义 NgModule 是一个带有 @NgModule 装饰器的类,代表一个模块. 2,作用 NgModule把组件.指令和管道打包成内聚的功能块,每个模块聚焦于一个特性区域.业 ...

  7. Angular ngModule简介

    上一篇我们已经配置好了angular本地的开发环境. 作为Angular10教程,在我的理解中,angular相较于VUE,它的模块化做得更好,这样使代码结构显得更清晰.所以本节,我们将简单介绍下an ...

  8. Angular @NgModule providers里multi等于true在源代码里如何体现的

    本文以SAP Spartacus的DefaultConfigurationChunk这个injection token 的注入为例: 因为是通过helper函数注入的,所以默认multi标志位为tru ...

  9. Angular NgModule里定义的注解和NgModuleRef$1运行时

    ExampleModule的declarations里有五个Components: 对应源代码中这五个Component: 6个imports: providers: 要获取更多Jerry的原创文章, ...

最新文章

  1. Apache commons-io
  2. 【Android 安装包优化】资源混淆 ( resources.arsc 资源映射表文件格式 | 头文件 数据格式 | 全局字符串池 数据格式 | 包数据 数据格式 | 包头 数据格式 )
  3. 自定义LOG投递OSS数据Partition,优化你的计算
  4. java列表框_Java图形用户界面之列表框
  5. lintcode 7. 二叉树的序列化和反序列化 Python代码
  6. 修改Typora的快捷键【markdown软件】
  7. thymeleaf 消息推送_Springboot集成WebSocket+Thymeleaf+Echarts完成数据的实时推送
  8. NER | 命名实体识别在QQ音乐上的应用
  9. java排序_Java实现九种排序算法3:插入排序之希尔排序
  10. Linux之LAMP架构
  11. Ubuntu系统各种文件的颜色代表的意义
  12. 常见的保障盘点结果准确性的方法和盘点差异的处理方法
  13. 情人节送什么礼物给女友比较好、这几款就够了
  14. 计算机视觉论文-2021-06-04
  15. 软测—直播教学 黑盒测试
  16. 走进JavaWeb技术世界12:从手动编译打包到项目构建工具Maven
  17. 二叉树已知前序遍历、中序遍历画出二叉树的形状
  18. hibernate一对一主键唯一外键关联(一)
  19. 15个nosql数据库
  20. 电视墙如何设计?上海极家装修怎么样?

热门文章

  1. java statemachine_StateMachine 状态机机制深入解析
  2. 前端开发的壁垒在哪儿?
  3. 了解 Diffing 算法
  4. dlib疲劳检测_使用OpenCv和Dlib进行打哈欠检测
  5. Android的TextView部分文字点击切换颜色(ClickableSpan)
  6. ajax没效果,ajax没有效果
  7. 变步长的梯形求积法matlab,基于MATLAB的变步长梯形数值积分法的研究与实验
  8. 服务器不支持mysql_服务器不支持 MySql 数据库的解决方法
  9. 2014全国计算机等级考试四级数据库工程师考试大纲,4月全国计算机等级考试四级数据库工程师笔试试卷(1)...
  10. 对left join on and、left join on where的理解