🎁 模块
NgModule 介绍
Angular是模块化的,拥有自己的模块系统,这就是 NgModule,一个 NgModule 就是一个容器,用于存放一些内聚的代码块,这些代码块专注于某个应用领域、某个工作流或一组紧密相关的功能。
NgModule 元数据
NgModule 用来控制组件、指令、管道等的可见性,处于同一个 NgModule 里面的组件默认互相可见,而对于外部的组件来说,只能看到 NgModule 导出( exports )的内容。也就是说,如果你定义的 NgModule 不 exports 任何内容,那么外部使用者即使 import 了你这个模块,也没法使用里面定义的任何内容。
NgModule 是 @angular/cli 打包的最小单位。
打包的时候,@angular/cli 会检查所有 @NgModule 和路由配置,如果你配置了异步模块,cli 会自动把模块切分成独立的 chunk(块)。这一点是和其它框架不同的,其它框架基本上都需要你自己去配置 webpack,自己定义切分 chunck 的规则;而在 Angular 里面,打包和切分的动作是 @angular/cli 自动处理的,不需要你干预。
NgModule 是 Router 进行异步加载的最小单位,Router 能加载的最小单位是模块,而不是组件。当然,模块里面只放一个组件是允许的,很多组件库都是这样做的。
⚠️ 注意
模块的定义方式会影响依赖注入机制:对于直接 import 的同步模块,无论你把 @Injectable 类型的组件定义在哪个模块里面,它都是全局可见的。
比如:在子模块 post.module.ts 的 providers 数组里面定义了一个 PostListService,你可能会觉得这个 PostListService 只有在 post.module.ts 里面可见。而事实并非如此, PostListService 是全局可见的,就相当于一个全局单例。
与此对应,如果你把 PostListService 定义到一个异步加载的模块里面,它就不是全局可见的了,因为对于异步加载进来的模块,Angular 会为它创建独立的 DI( Dependecy Injection,依赖注入)上下文。
所以,如果你想让 PostListService 全局可见,应该把它定义在根模块 app.module 里面。同时要特别注意,如果你希望 PostListService 是全局单例的,只能在 app.module 里面的 providers 数组里面定义一次,而不能在其它模块里面再次定义,否则就会出现多个不同的实例。
利用这个特性,我们通常把整个应用的 service 都放在一个 shareModule 里面,其他模块需要使用这些服务的时候,只需要import shareModule 就可以了,即使shareModule并没有exports 这些service,import shareModule就可以使用这些service。而shareModule里面如果有declaration 一个组件,但是如果shareModule 没有exports 这个组件,即使你import shareModule,也不能使用shareModule 的这个组件。
NgModule 和组件
NgModule 为其中的组件提供了一个编译上下文环境 。根模块总会有一个根组件,并在引导期间创建它。但是,任何模块都能包含任意数量的其它组件,这些组件可以通过路由器加载,也可以通过模板创建。那些属于这个 NgModule 的组件会共享同一个编译上下文环境。
组件及其模板共同定义视图。组件还可以包含视图层次结构,它能让你定义任意复杂的屏幕区域,可以将其作为一个整体进行创建、修改和销毁。一个视图层次结构中可以混合使用由不同 NgModule中的组件定义的视图。这种情况很常见,特别是对一些 UI 库来说。

当你创建一个组件时,它直接与一个叫做宿主视图的视图关联起来。宿主视图可以是视图层次结构的根,该视图层次结构可以包含一些内嵌视图,这些内嵌视图又是其它组件的宿主视图。这些组件可以位于相同的 NgModule 中,也可以从其它 NgModule 中导入。树中的视图可以嵌套到任意深度。