[Angular] Day30. Angular Module(一)

在 Angular 中应用程序是模块化的,Angular 有自己的模块化系统称为 NgModule,他可以包含 componentservice providers 和其他程序的文件,还可以导入从其他 NgModules 导出的功能,并导出显订的功能以提供给其他 NgModules 使用,每一个 Angular app 都至少有一个 NgModule 他被称为 root module,通常会叫做 AppModule

虽然一个小的专案中可能会只有一个 module (root module),但在大多数大型的专案中都会建立多个 modules 用於将功能分割,达到将专案模块化的目的。

https://ithelp.ithome.com.tw/upload/images/20210829/20124767ZK4CbvmUSc.jpg


NgModule metadata

NgModule 是由一个用 @NgModule() 装饰的 class,@NgModule() 是一个接收单个 metadata 物件的函数,它具有以下的 properties:

  • declarations:属於这个 NgModule 的 component、pipes 或 directive。
  • exports:要导出的功能,可以让其他的 NgModules 引入并且使用
  • imports:引入来自其他 NgModules 所导出的功能
  • providers:在这个 NgModule 中所使用的所有 Service
  • bootstrap:主应用程序的 view 称为 root component,他乘载着所有应用程序的 view,简单来说就是定义应用程序中 view 的最上层 component

metadata 提供了该如何编译 component 的 template 以及如何在运行时创建注入器 ( injector ),它识别了 NgModule 的 component、pipe 或 directive,并通过 exports property 将需要给其他 NgModules 使用的部分公开,还可以使用 NgModule 为 service 添加 provide ,以便 serviec 在其那地方也可以被使用。

可以在 metadata 中的 declarations 中定义哪些 component、pipes 或 directive 属於这个 Module。

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

通常 root module 不需要设置 exports property,因为没有其他的 NgModules 需要引入 root module。

The declarations array

在 NgModule 的 declarations 中定义了哪些 componentpipesdirective 属於这个 Module,当你使用 Angular CLI 创建新的 component 时会自动将新的 component 添加到这里,所以如果是手动建立新的 component 时要记得要把他添加到这里,相对的如果移除一个 component 也要记得将它从 declarations 中移除。

如果你使用一个没有声明的 component 则 Angular 会返回错误,而 declarations 只能接收可声明的,可声明的包括 componentpipesdirective ,Module 的所有声明项都必须在 declarations 当中,而这个 declarations 只能属於一个 Module 不然会有多重宣告的错误,这些声明项只有在 Module 中是可见的,除非将他们使用 export 导出,否则在其他地方是不可见的。

The imports array

imports 只出现在 @NgModule() 的 metadata 中,它告诉 Angular 这个 Module 的运行需要使用到其他 Modules 的内容,当在此 Module 中引入其他 Module 所导出的内容时,该 Module 中的 component 就可以使用别的 Module 导出的 component、pipe 或 directive。

The bootstrap array

Angular 的应用程序透过 root module 启动,也称为 entryComponent,在创建应用程序的过程中会创建 bootstrap 中的 component 并将它插入浏览器的 DOM 中,每一个 bootstrapped component 都是本身 component tree 的基础(最上层的 component),所以当插入一个 bootstrapped component 後会触发一连串的 component 创建然後填充本身 component tree。

大多数应用程序都只有一个 component tree 并引导单个 root component,但是你也可以创建多个 component tree 来扩大你的应用程序,而这个 root component 通常称为 AppComponent 并位於 root module 的 bootstrap 中。


NgModules and components

NgModule 为 component 提供了 compilation context,简单来说 NgModule 提供了创建 component 所需要的所有资讯,除了 root module 总是有一个 root component 之外,其他的任何 NgModules 都可以有任意数量的 component,而这个 NgModule 会为他底下的所有 component 提供 compilation context。

https://ithelp.ithome.com.tw/upload/images/20210829/20124767olOJtbHwPU.png

一个 component 和他的 template 定义了一个 view,但是一个 component 可以包含一个有层次结构的 view,他允许你创建、修改或销毁一个嵌入式 view,而层次结构的 view 可以混合在属於不同 NgModules 的 component 中定义的 view,简单来说就是你可以在 componentA 的 view 中创建一个属於另一个 NgModule 的 componentB 的 view。

https://ithelp.ithome.com.tw/upload/images/20210829/20124767Cmvc8VcS1N.png

当你嵌入一个 view 时,他会直接与一个称为 host view 的 view 进行连接,host view 可以是这组 view 的 root,他可以包含所有嵌入的 view,就算这些 view 是来自别的 NgModules 也可以,而嵌入的 view 又可以在嵌入自己的 view,所以可以嵌套到任一深度。


JavaScript modules vs. NgModules

在 Javascript ES6 中引入了属於 Javascript 的 module 系统,虽然了解 Javascript 的 Module 对於了解 Angular 的 Module 很有帮助,但还是与 NgModule 有一些不同的地方,不过 Angular 同时使用了这两种 Module 的概念

JavaScript modules: Files containing code

Javascript Module 是带有 Javascript 程序的单个文件,通常包含用於应用程序中特定目的的 class 或 library,Javascript 的 Module 可以让你将工作分散到多个档案中。

要让 Module 中的内容提供给其他 Module 使用,要在这个 Module 的末尾使用 export 语法,比如说

export class AppComponent { ... }

而要使用其他 Module 中的内容时,要使用 import 将它导入到自身 Module 中

import { AppComponent } from './app.component';

每一个 Module 都有自己的 top level variable,简单来说就是每一个 Module 中的 variable 或 method 在别的 Module 或地方是看不到的,每一个 Module 都有自己的命名空间以防止他们与其他 Module 中的内容发生冲突。


Frequently-used modules

一个 Angular 应用程序至少需要一个 Module 作为 root module,当像应用程序添加新功能时,可以将这些功能对应的 Module 添加到你的 Module 中,下面介绍一些经常使用的 Angular module 以及他们所包含的一些内容

NgModule Import it from Why you use it
BrowserModule @angular/platform-browser 当你想在浏览器中运行你的专案
CommonModule @angular/common 当你需要使用 NgIfNgFor 等等
FormsModule @angular/forms 当你想要构建 template-driven forms 时
ReactiveFormsModule @angular/forms 当你想要构建 reactive form 时
RouterModule @angular/router 当你要使用 RouterLink, .forRoot().forChild()
HttpClientModule @angular/common/http 当你需要与 server 沟通时

BrowserModule and CommonModule

在 BrowserModule 中导入了 CommonModule,它提供了许多常用的 directive 例如 ngIf, ngFor 等等,此外 BrowserModule 重新的导出了 CommonModule 这使其所有的 directive 可以用於导入 BrowserModule 中。

对於在浏览器中运行的应用程序而言,在 root module 中导入 BrowserModule 是必须的,因为它提供了启动和运行浏览器应用程序必不可少的服务,他是为整个应用程序所提供的,所以他只应该在 root module 中被导入,所以不可以将它放在延迟加载的 feature module 中,因为会造成错误。

https://ithelp.ithome.com.tw/upload/images/20210829/20124767kFmuniMozj.png


Guidelines for creating NgModules

通常再开发 Angular 的应用程序时会因为不同的目的创建不同的 NgModule,NgModule 是组织程序并将与特定功能或特性相关的程序与其他部分分开,使用 NgModule 将 component、pipe 和 directive 合并回内聚的功能模组,将一个功能模组集中在服务某一个功能或业务上面,所以通常会建立下面几种类型的 NgModule

Domain NgModules

使用 Domain NgModule 通常是为了专门提供於特定功能或应用程序的用户体验,比如说编辑客户或下订单,他组织与某个功能相关的程序,包括构建该功能的所有 component、routing 和 template。

通常会将最上层的 component 或 root component 作为唯一导出的 component,Domain NgModule 主要由 declarations 所组成。

Routed NgModules

对所有 lazy-loaded NgModule 使用 Routed NgModule,使用 Routed NgModule 的最顶层 component 作为路由器导航的路径,而 Routed NgModule 不会导出任何内容,因为他们的 component 永远不会出现在其他 component 的 template 中。

还要注意不要将 lazy-loaded NgModule 导入另一个 NgModule 中,因为这样会会立马将这个 Module 载入到应用程序中,就失去了 lazy-loaded 的目的了。

Routed NgModule 只需要很少的 providers,因为对於应用程序而言只有在需要的时候才会载入 Routed NgModule,所以如果在他的 providers 中填入 service 的话将会变得不可用,因为 injector 不知道 lazy-loaded NgModule。

Routing NgModules

Routing NgModuleDomain NgModule 提供路由配置,通常会由 Routing NgModule 完成以下任务

  • 定义路由
  • 将路由器配置添加到 NgModule 的 imports 中
  • 向 NgModule 的 providers 提供 service

Routing NgModule 的名称应该与其同伴的 NgModule 名称平行,比如说 contact.module.ts 中的 ContactModule 在 contact-routing.module.ts 中有一个名为 ContactRoutingModule 的 Routing NgModule。

如果 Routing NgModule 是 root App Module 则 AppRoutingModule 是使用 RouterModule.forRoot(routes) 将路由器配置添加在 imports 中

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

而如果是其他 Routing NgModule 都是使用 RouterModule.forChild(routes) 将路由器配置添加到他们的 imports 中。

要注意不要在 Routing NgModule 中使用 declarations,component、pipe 或 directive 的声明是在伴随的 Domain NgModule 中声明的。

Service NgModules

Service NgModule 用於提供实用的服务,例如数据的访问或消息的传递,理想的 Service NgModule 应该全由 providers 所组成,如果要创建 Service NgModule 可以查看 Angular 的 HttpClientModule 他是一个非常好的例子,要注意的是请只在 root AppModule 中导入所有的 Service NgModules

Widget NgModules

使用 Widget NgModule 使 component、pipe 或 directive 可被外部的 NgModule 给使用,当某个 NgModule 中的 component 需要使用到 Widget NgModule 中的某一个 component 或功能时,将

Widget NgModule 到入到这个 NgModule 中即可,需多第三方 UI component libaray 都是作为 Widget NgModule 然後提供给各个 NgModule 中, 一个 Widget NgModule 应该完全declarations 所组成,其中大部分都是导出的这样才能让其他地方使用。

Shared NgModules

最後介绍 Shared NgModule 他是将常用的 directive、pipe 或 component 统一放入一个 NgModule 中,通常会命名为 SharedModule,然後再应用程序的其他部分需要时导入,这样只导入一个 Module 就可以获得许多功能。

Shared NgModule 不应该包含 providers,任何其导入或导出的 NgModule 都不应该包含 providers。


结论

本篇中介绍了 Angular 的 NgModule 的观念,NgModule 中 metadata 是由 declarationsexportsimportsprovidersbootstrap 所组成

  • declarations 是用於宣告属於这个 NgModule 的 component、pipe 或 directive
  • exports 是用於将这个 NgModule 需要给其他 NgModule 使用的部分导出
  • imports 是用於引入其他 NgModule 导出的功能
  • providers 是用於提供 NgModule 所使用的 Services
  • bootstrap 是用於定义应用程序的 entryComponent

也介绍了 Angular 同时使用了 Javascript Module 与 NgModule 的概念,对於一个档案中的内容要导出的给其他部分使用的话要使用 export,而要使用别的档案中提供的内容时需要使用 import 将它导入。

最後介绍了再开发 Angular 专案时会建立的几种 NgModule

  • Domain 是围绕功能或用户体验所组成的
  • Routed 中的顶部 component 充当路由器导航路径的目的地
  • Routing 是为另一个 NgModule 提供路由配置的
  • Service 是提供实用服务的,例如数据访问或消息传达
  • Widget 是将某一部分的 component, pipe, directive 所组合起来让其他 NgModule 可以使用,常用於第三方 UI library
  • Shared 是将共享的所有 component, pipe, directive 所组合,当其他 NgModule 要使用时引入它即可

Angular Module 的观念还有很多,由於文章长度的问题没办法一次讲完,所以一样将他分为两段这样可以介绍的比较完整,下半部的 NgModule 就明天见吧


Reference


<<:  #30 [Final] Had Fun Learning JavaScript?

>>:  Day16-Redux 篇-认识 Redux Toolkit

Android Studio - LOG

安卓有五种LOG的方式 搭配logcat 标记出你想知道的讯息 可以提升侦错的效率和优化程序码 Lo...

【DAY 01】 学习网页的第一步

前言 不管你是不是学程序的,常常都会接触到网页,常常会听到网页就是HTML、CSS和JavaScri...

Day 14 Azure cognitive service: Text-to-Speech- Azure 念给你听

Azure cognitive service: Text-to-Speech- Azure 念给你...

D18 - 彭彭的课程# Python Package 封包的设计与使用

今天讲自制模组如何呼叫使用 link:https://www.youtube.com/watch?v...

Day37 ( 电子元件 ) 超音波倒车雷达

超音波倒车雷达 教学原文参考:超音波倒车雷达 这篇文章会介绍如何使用超音波感测器和蜂鸣器,搭配「变数...