Skip to main content

模块

模块是用@Module()装饰器注解的类@Module()装饰器提供Nest用于组织应用程序结构的元数据

Nest.js模块

每个应用程序至少有一个模块,即根模块根模块是Nest用于构建应用程序图的起点- Nest用于解析模块和提供者关系及依赖关系的内部数据结构。 虽然理论上非常小的应用程序可能只有根模块,但这不是典型情况。 我们要强调的是,强烈建议将模块作为组织组件的有效方式。 因此,对于大多数应用程序来说,最终的架构将采用多个模块, 每个模块封装一组密切相关的功能。

@Module()装饰器采用单个对象其属性描述模块

providers将由Nest注入器实例化的提供程序,并且至少可以在该模块中共享
controllers该模块中定义的必须实例化的控制器集
imports导入模块的列表,导出该模块所需的提供者程序
exports此模块提供的提供程序子集,并且应该在导入此模块的其他模块中可用

该模块默认封装了提供者。 这意味着不可能注入即不直接属于当前模块不从导入模块导出的提供者程序。 因此,您可以将从模块导出的提供程序视为模块的公共接口或API

功能模块

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

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

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

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

要使用CLI创建模块,只需执行nest g module cats

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

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

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

export class AppModule {}

这是我们的目录结构现在的样子:

src
|--cats
|--dto
|--create-cat.dto.ts
|--interfaces
|--cat.interface.ts
|--cats.controller.ts
|--cats.module.ts
|--cats.service.ts
|--app.module.ts
|--main.ts

共享模块

在Nest中,默认情况下模块是单例,因此您可以轻松地在多个模块之间共享任何提供者程序的同一实例

Nest.js 共享模块

每个模块自动成为共享模块。创建后,任何模块都可以重用它。 假设我们想要在其他几个模块之间共享CatsService的实例。 为此,我们首先需要将将CatsService提供者程序添加到模块的exports数组中来导出它,如下所示:

cats.module.ts
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并将于导入它的所有其他模块共享同一个实例

模块再次导出

如上所示,模块可以导出其内部提供者程序。此外,他们还可以重新导出导入的模块。 在下面的示例中,CommonModule被导入到CoreModule中导出,使其可用于导入此模块的其他模块。

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

依赖注入

模块类也可以注入提供者程序(例如,出于配置目的):

cats.module.ts
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) {}
}

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

全局模块

如果您必须在各处导入相同的模块集,这可能会变得乏味。 与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: [CatsServicer],
exports: [CatsServicer],
})

export class CatsModule {}

@Global()装饰器使模块具有全局范围全局模块应该只注册一次,通常由根模块或核心模块注册。 在上面的示例中,CatsService提供者程序将无处不在,并且希望注入服务的模块不需要再其导入数组 中导入CatsModule

tip

让一切全局化并不是一个好的设计决策。全局模块可用于减少必要的样板数量。 imports数组通常是使模块的API可供消费者使用的首选方式。

动态模块

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,
};
}
}
tip

forRoot()方法可以同步或异步(即通过Promise)返回动态模块

该模块默认定义了连接提供者程序(在@Module()类装饰器元数据中),但还会根据传入forRoot()方法的实体 和可选对象,公开一系列提供者程序,例如存储库。请注意,动态模块返回的属性扩展(而不是覆盖)@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 {}