5. Nest.js Modules

Module是一个带有@Module()装饰器的类。@Module()装饰器提供了Nest用来组织应用程序结构的元数据。

image.png

@Module()装饰器接受一个对象,该对象的属性描述了该模块:

PropertiesDescription
Providers这些提供商将被Nest注入器实例化,并且至少可以在整个模块中共享
controllers在此模块中定义的控制器集,必须实例化
imports导出该模块中所需的provider的导入模块列表
exports提供程序的子集,由该模块提供,并且应该在导入该模块的其他模块中可用。您既可以使用提供者本身,也可以只使用它的令牌(提供值)

该模块默认封装provider。这意味着不可能注入既不是当前模块的直接组成部分,也不是从导入模块导出的provider。因此,您可以将从模块导出的provider视为模块的公共接口或API。

Feature modules

CatsControllerCatsService属于同一个应用程序域。由于它们密切相关,因此将它们移到特性模块中是有意义的。功能模块只是组织与特定功能相关的代码,保持代码的组织性并建立清晰的边界。这有助于我们管理复杂性,并使用SOLID原则进行开发,特别是当应用程序和/或团队的规模增长时。

为了演示这一点,我们将创建CatsModule

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {}

[!hint]
要使用CLI创建一个模块,只需执行$ nest g module cats命令。

上面,我们在cats.module.ts文件中定义了CatsModule,并将与该模块相关的所有内容移动到cats目录中。我们需要做的最后一件事是将这个模块导入根模块(AppModule,定义在app.module.ts文件中)。

import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';

@Module({
  imports: [CatsModule],
})
export class AppModule {}

下面是我们的目录结构:

image.png

Shared modules

在Nest中,模块默认是单例的,因此你可以毫不费力地在多个模块之间共享任何提供者的相同实例。

image.png

每个模块自动成为一个共享模块。一旦创建,它就可以被任何模块重用。假设我们想要在其他几个模块之间共享CatsService的一个实例。为了做到这一点,我们首先需要通过将其添加到模块的exports数组中来导出CatsService提供商,如下所示:

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService]
})
export class CatsModule {}

现在,任何导入CatsModule的模块都可以访问CatsService,并将与导入它的所有其他模块共享同一个实例。

Module re-exporting

如上所述,模块可以导出它们的内部provider。此外,它们还可以重新导出它们导入的模块。在下面的例子中,CommonModule被导入CoreModule并从CoreModule中导出,使得它可以被其他导入这个模块的模块使用。

@Module({
  imports: [CommonModule],
  exports: [CommonModule],
})
export class CoreModule {}

Dependency injection

模块类也可以注入provider(例如,用于配置目的):

import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class CatsModule {
  constructor(private catsService: CatsService) {}
}

然而,由于循环依赖,模块类本身不能作为provider注入。

Global modules

如果必须在任何地方导入相同的模块集,就会变得很繁琐。与Nest不同,angular的providers是在全局作用域中注册的。一旦定义,它们就随处可见。然而,Nest将提供程序封装在模块作用域中。如果不先导入封装模块,就不能在其他地方使用模块的提供程序。

当你想要提供一组在任何地方都可以使用的提供程序(例如,帮助程序、数据库连接等)时,使用@Global()装饰器将模块设置为全局。

import { Module, Global } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

@Global()
@Module({
  controllers: [CatsController],
  providers: [CatsService],
  exports: [CatsService],
})
export class CatsModule {}

@Global()装饰器使模块成为全局作用域。全局模块应该只注册一次,通常由根模块或核心模块注册。在上面的例子中,CatsService提供者将是无处不在的,并且希望注入该服务的模块将不需要在其imports数组中导入CatsModule

[!hint]
让所有东西都是全局的并不是一个好的设计决策。可以使用全局模块来减少必要的样板文件的数量。imports数组通常是让消费者可以使用模块API的首选方式。

Dynamic modules

Nest模块系统包括一个强大的功能,称为动态模块。此特性使您能够轻松地创建可定制的模块,这些模块可以动态地注册和配置提供程序。动态模块在这里有广泛的介绍。在本章中,我们将给出一个简短的概述来完成对模块的介绍。

下面是一个DatabaseModule动态模块定义的例子:

import { Module, DynamicModule } from '@nestjs/common';
import { createDatabaseProviders } from './database.providers';
import { Connection } from './connection.provider';

@Module({
  providers: [Connection],
})
export class DatabaseModule {
  static forRoot(entities = [], options?): DynamicModule {
    const providers = createDatabaseProviders(options, entities);
    return {
      module: DatabaseModule,
      providers: providers,
      exports: providers,
    };
  }
}

[!hint]
forRoot()方法可以同步或异步(即通过Promise)返回一个动态模块。

该模块在默认情况下定义了Connection provider(在@Module()装饰器元数据中),但另外——取决于传递给forRoot()方法的entitiesoptions对象——还公开了一个提供程序集合,例如存储库。注意,动态模块返回的属性扩展(而不是覆盖)了在@Module()装饰器中定义的基本模块元数据。这就是从模块导出静态声明的Connection提供程序和动态生成的存储库提供程序的方式。

如果要在全局作用域中注册一个动态模块,请将global属性设置为true

{
  global: true,
  module: DatabaseModule,
  providers: providers,
  exports: providers,
}

[!warning]
正如上面所提到的,让所有东西都是全局的并不是一个好的设计决策。

DatabaseModule可以通过以下方式导入和配置:

import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';
import { User } from './users/entities/user.entity';

@Module({
  imports: [DatabaseModule.forRoot([User])],
})
export class AppModule {}

如果你想反过来重新导出一个动态模块,你可以忽略exports数组中的forRoot()方法调用:

import { Module } from '@nestjs/common';
import { DatabaseModule } from './database/database.module';
import { User } from './users/entities/user.entity';

@Module({
  imports: [DatabaseModule.forRoot([User])],
  exports: [DatabaseModule],
})
export class AppModule {}

动态模块一章更详细地介绍了这个主题,并包括一个工作示例

[!hint]
本章中,学习如何使用ConfigurableModuleBuilder构建高度可定制的动态模块。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值