(三)创建功能模块

原文链接:https://angular-2-training-book.rangle.io/handout/modules/feature-modules.html

当我们的根模块开始增长时,最开始显而易见的是,某些元素(组件,指令等)在某方面相关联,几乎感觉他们属于可以被“插入”的库。

在我们前面的例子中,我们开始看到了。 我们的根模块具有组件,管道和服务,其唯一目的是处理信用卡。 如果我们将这三个元素提取到自己的特征模块 ,然后将其导入到我们的根模块会怎么样?

我们要做到这一点。 第一步是创建两个文件夹,以将属于根模块的元素与属于功能模块的元素区分开来。

.
├── app
│   ├── app.component.ts
│   └── app.module.ts
├── credit-card
│   ├── credit-card-mask.pipe.ts
│   ├── credit-card.component.ts
│   ├── credit-card.module.ts
│   └── credit-card.service.ts
├── index.html
└── main.ts

请注意每个文件夹如何拥有自己的模块文件: app.module.ts和credit-card.module.ts 。 我们首先关注后者。

credit-card/credit-card.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';

import { CreditCardMaskPipe } from './credit-card-mask.pipe';
import { CreditCardService } from './credit-card.service';
import { CreditCardComponent } from './credit-card.component';

@NgModule({
  imports: [CommonModule],
  declarations: [
    CreditCardMaskPipe,
    CreditCardComponent
  ],
  providers: [CreditCardService],
  exports: [CreditCardComponent]
})
export class CreditCardModule {}

我们的功能CreditCardModule它非常类似于根模块AppModule,但还是有一些重要的区别:

*我们没有导入BrowserModule而是CommonModule 。 如果我们在这里可以看到BrowserModule的文档,我们可以看到它是通过许多其他服务重新导出CommonModule ,有助于在浏览器中渲染Angular应用程序。 这些服务将我们的根模块与特定平台(浏览器)相结合,但我们希望我们的功能模块是独立于平台的。 这就是为什么我们只在那里导入CommonModule ,它只导出通用的指令和管道。

当涉及组件,管道和指令时,如果在根模块或任何其他功能模块中导入相同的依赖项,则每个模块都应导入自己的依赖项。 简而言之,即使有多个功能模块,每个功能模块都需要导入CommonModule 。 

我们正在使用一种名为exports的新属性。 默认情况下 , declarations数组中定义的每个元素都是私有的 。 我们应该只输出我们应用程序中执行其他任务的其他模块。 在我们的例子中,我们只需要使CreditCardComponent可见,因为它被用在AppComponent的模板中。 

app/app.component.ts

...
@Component({
  ...
  template: `
    ...
    <app-credit-card></app-credit-card>
  `
})
export class AppComponent {}
我们正在将CreditCardMaskPipe保留为私有,因为它仅在CreditCardModule中使用 ,没有被其他模块直接使用。

我们现在可以将此功能模块导入到我们简化的根模块中。

app/app.module.ts

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

import { CreditCardModule } from '../credit-card/credit-card.module';
import { AppComponent } from './app.component';

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

到这一步,我们完成了,我们的应用程序如预期的一样。

查看示例

服务和延迟加载模块

这是Angular模块的棘手部分。 除非明确导出组件,管道和指令的范围,否则服务是全局可用的,除非模块被懒加载。

很难理解,我们首先让我们尝试在我们的示例中看到CreditCardService发生了什么。 请注意,该服务不在exports数组中,而是在providers数组中。 通过这种配置,即使在另一个模块中的AppComponent中,我们的服务也将随处可见。 所以,即使使用模块,也没有办法拥有一个“私有”服务,除非这个模块被懒加载。

当模块被懒惰加载时,Angular将创建一个子注入器(它是根模块的根注射器的子代),并且将在那里创建我们的服务实例。

想象一下,我们的CreditCardModule被配置为懒惰加载。 使用我们当前的配置,当应用程序被引导并且我们的根模块加载到内存中时,将会将CreditCardService (单例)的实例添加到根注入器中。 但是,当CreditCardModule在将来某个时候被懒惰加载时,将为该模块创建一个新的CreditCardService 实例的子注入器。 在这一点上,我们有一个分层注入器,具有相同服务的两个实例 ,这通常不是我们想要的。

想想一个执行身份验证的服务的示例。 我们希望在整个应用程序中只有一个单例,不管我们的模块是在bootstrap还是lazy加载的时候加载。 因此,为了使我们的功能模块的服务只添加到根注射器,我们需要使用不同的方法。

credit-card/credit-card.module.ts

import { NgModule, ModuleWithProviders } from '@angular/core';
/* ...other imports... */

@NgModule({
  imports: [CommonModule],
  declarations: [
    CreditCardMaskPipe,
    CreditCardComponent
  ],
  exports: [CreditCardComponent]
})
export class CreditCardModule {
  static forRoot(): ModuleWithProviders {
    return {
      ngModule: CreditCardModule,
      providers: [CreditCardService]
    }
  }
}

不同于以前,我们并没有把我们的服务直接放在NgModule装饰器的providers属性中 。 这一次我们定义了一个名为forRoot的静态方法,通过它定义要导出的模块和服务。

使用这种新语法,我们的根模块略有不同。

app/app.module.ts

/* ...imports... */

@NgModule({
  imports: [
    BrowserModule,
    CreditCardModule.forRoot()
  ],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }

你能发现差异吗? 我们不直接导入CreditCardModule ,而是导入从forRoot方法返回的对象,其中包括CreditCardService 。 虽然这种语法比原始语法更复杂一些,但它将保证我们只有一个CreditCardService实例被添加到根模块中。 当加载CreditCardModule (甚至延迟加载)时,不会将该服务的新实例添加到子注入器。

查看示例

作为经验法则, 在从功能模块导出服务时始终使用forRoot语法 ,除非您有非常特殊的需求,需要在依赖注入树的不同级别进行多个实例。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值