深度刨析NgModule

1. 前言

我们今天要学习的是 Angular2 的模块系统,一般情况下我们使用一个根模块去启动我们的应用,然后使用许多的功能模块去丰富我们的应用,扩展我们应用的功能.这些全部依靠我们的 NgModule 装饰器,接下来我们就来好好学习一下这个装饰器. 当然在这个过程中你会遇到一些新的指令,概念等等;但是别慌,我们会在以后的文章中一个一个的详细讲解呢.

2. @NgModule 的重要作用

在 Angular 中,NgModule 有以下几个重要的作用:

NgModule 最根本的意义是帮助开发者组织业务代码,开发者可以利用 NgModule 把关系比较紧密的组件组织到一起,这是首要的。
NgModule 用来控制组件、指令、管道等的可见性,处于同一个 NgModule 里面的组件默认互相可见,而对于外部的组件来说,只能看到 NgModule 导出( exports )的内容 这一特性非常类似 Java 里面 package 的概念。
也就是说,如果你定义的 NgModule 不 exports 任何内容,那么外部使用者即使 import 了你这个模块,也没法使用里面定义的任何内容。

3. NgModule API

在进入详细讲解之前, 我们首先来熟悉一下 NgModule 的 API.

NgModule 源码位于 angular 项目的 packages\core\src\metadata\ng_module.ts 文件中.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
interface NgModule {
// providers: 需要使用的 Service 都放在这里. 这个选项是一个数组,需要我们列出我们这个模块的一些需要共用的服务
// 然后我们就可以在这个模块的各个组件中通过依赖注入使用了.
providers: Provider[];
// declarations: 数组类型的选项, 用来声明属于这个模块的指令,管道等等.
// 然后我们就可以在这个模块中使用它们了.
// (Angular中没有模块级别的service,所有在NgModule中声明的Provider都是注册在根级别的Dependency Injector中)
declarations: Array<Type<any> | any[]>;
// imports: 用来导入外部模块。数组类型的选项,我们的模块需要依赖的一些外部模块,这样做的目的使我们这个模块
// 可以直接使用别的模块提供的一些指令,组件等等.
imports: Array<Type<any> | ModuleWithProviders | any[]>;
// exports: 数组类型的选项,我们这个模块需要导出的一些组件,指令,模块等;
// 如果别的模块导入了我们这个模块,
// 那么别的模块就可以直接使用我们在这里导出的组件,指令模块等.
exports: Array<Type<any> | any[]>;
// entryComponents: 数组类型的选项,指定一系列的组件,这些组件将会在这个模块定义的时候进行编译
// Angular会为每一个组件创建一个ComponentFactory然后把它存储在ComponentFactoryResolver
entryComponents: Array<Type<any> | any[]>;
// bootstrap: 定义启动组件。数组类型选项, 指定了这个模块启动的时候应该启动的组件.当然这些组件会被自动的加入到entryComponents中去
bootstrap: Array<Type<any> | any[]>;
// schemas: 不属于Angular的组件或者指令的元素或者属性都需要在这里进行声明.
schemas: Array<SchemaMetadata | any[]>;
// id: 字符串类型的选项,模块的隐藏ID,它可以是一个名字或者一个路径;用来在getModuleFactory区别模块,如果这个属性是undefined
// 那么这个模块将不会被注册.
id: string;
// jit存在时,AOT编译器将忽略此模块。
// 它保留在分布式代码中,JIT编译器会尝试编译它
// 在运行时,在浏览器中。
// 为了确保正确的行为,应用程序必须导入“@angular/compiler”。
jit: boolean;
}

angular 15 以前, 每个应用至少有一个根模块,按照惯例,根模块的名字一般都叫 AppModule,如果你没有非常特别的理由,就不要随意改这个名字了,这相当于一个惯例。
Angular 15 及以后版本可以定义 standalone 组件, 可以不隶属于任何模块.

NgModule 和 ES6 Module

  • ES6 里面的模块是通过 export 和 import 来进行声明的,它们是语法层面的内容;而 NgModule 完全不是这个概念,从上面的作用列表你也能看出来。最重要的一点,目前,ES6 里面的 import 只能静态引入模块,并不能异步动态加载模块。

  • NgModule 可以配合 Router 来进行异步模块加载。
    模块的定义方式会影响依赖注入机制:
    对于直接 import 的同步模块,无论你把 @Injectable 类型的组件定义在哪个模块里面,它都是全局可见的。

6. Angular 系列文章

最新更新以及更多 Angular 相关文章请访问 Angular 合集 | 鹏叔的技术博客

5. 参考文档

ANGULAR2 NGMODULE 模块详解

angular 模块 @NgModule 的使用及理解