🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 模块 模块是具有 `@Module()` 装饰器的类。 `@Module()` 装饰器提供了元数据,Nest 用它来组织应用程序结构。 ![图1](https://docs.nestjs.com/assets/Modules_1.png) 每个 Nest 应用程序至少有一个模块,即根模块。根模块是 Nest 开始安排应用程序树的地方。事实上,根模块可能是应用程序中唯一的模块,特别是当应用程序很小时,但是对于大型程序来说这是没有意义的。在大多数情况下,您将拥有多个模块,每个模块都有一组紧密相关的**功能**。 `@module()` 装饰器接受一个描述模块属性的对象: ||| |:-----:|:-----:| |providers| 由 Nest 注入器实例化的提供者,并且可以至少在整个模块中共享| |controllers|必须创建的一组控制器| |imports|导入模块的列表,这些模块导出了此模块中所需提供者| |exports|由本模块提供并应在其他模块中可用的提供者的子集。| 默认情况下,该模块**封装**提供程序。这意味着无法注入既不是当前模块的直接组成部分,也不是从导入的模块导出的提供程序。因此,您可以将从模块导出的提供程序视为模块的公共接口或API。 ## 功能模块 `CatsController` 和 `CatsService` 属于同一个应用程序域。 应该考虑将它们移动到一个功能模块下,即 `CatsModule`。 > cats/cats.module.ts ```typescript import { Module } from '@nestjs/common'; import { CatsController } from './cats.controller'; import { CatsService } from './cats.service'; @Module({ controllers: [CatsController], providers: [CatsService], }) export class CatsModule {} ``` > 要使用 CLI 创建模块,只需执行 `$ nest g module cats` 命令。 我已经创建了 `cats.module.ts` 文件,并把与这个模块相关的所有东西都移到了 cats 目录下。我们需要做的最后一件事是将这个模块导入根模块 `(ApplicationModule)`。 > app.module.ts ```typescript import { Module } from '@nestjs/common'; import { CatsModule } from './cats/cats.module'; @Module({ imports: [CatsModule], }) export class ApplicationModule {} ``` 现在 `Nest` 知道除了 `ApplicationModule` 之外,注册 `CatsModule` 也是非常重要的。 这就是我们现在的目录结构: ```text src ├──cats │ ├──dto │ │ └──create-cat.dto.ts │ ├──interfaces │ │ └──cat.interface.ts │ ├─cats.service.ts │ ├─cats.controller.ts │ └──cats.module.ts ├──app.module.ts └──main.ts ``` ## 共享模块 在 Nest 中,默认情况下,模块是**单例**,因此您可以轻松地在多个模块之间共享**同一个**提供者实例。 ![图1](https://docs.nestjs.com/assets/Shared_Module_1.png) 实际上,每个模块都是一个**共享模块**。一旦创建就能被任意模块重复使用。假设我们将在几个模块之间共享 `CatsService` 实例。 我们需要把 `CatsService` 放到 `exports` 数组中,如下所示: > cats.module.ts ```typescript 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` ,并且它们将共享相同的 `CatsService` 实例。 ## 模块导出 如上所示,模块可以导出其内部提供者。此外,他们可以重新导出他们导入的模块。在下面的示例中,既可以导入`CommonModule`**也**可以导出`CoreModule`,使其可用于导入该模块的其他模块。 ```typescript @Module({ imports: [CommonModule], exports: [CommonModule], }) export class CoreModule {} ``` # 依赖注入 提供者也可以注入到模块(类)中(例如,用于配置目的): > cats.module.ts ```typescript 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 readonly catsService: CatsService) {} } ``` 但是,由于[循环依赖](https://docs.nestjs.com/fundamentals/circular-dependency)性,模块类不能注入到提供者中。 ## 全局模块 如果你不得不在任何地方导入相同的模块,那可能很烦人。在 [Angular](https://angular.io) 中,提供者是在全局范围内注册的。一旦定义,他们到处可用。如果不首先导入封装模块,您将无法在其他地方使用模块的提供程序。但是有时候,你可能只想提供一组随时可用的东西 - 例如:helper,数据库连接等等,请使用`@Global()`装饰器使模块成为**全局的**。这就是为什么你能够使模块成为全局模块。 ```typescript 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` 组件将无处不在,而想要使用 `CatsService` 的模块则不需要在 `imports` 数组中导入 `CatsModule`。 > 使一切全局化并不是一个好的解决方案。 全局模块可用于减少必要模板文件的数量。 `imports` 数组仍然是使模块 API 透明的最佳方式。 ## 动态模块 `Nest` 模块系统包括一个称为动态模块的强大功能。此功能使您可以轻松创建可自定义的模块,这些模块可以动态注册和配置提供程序。动态模块在这里广泛介绍。在[本章](/8/fundamentals/dynamic-modules)中,我们将简要概述以完成模块介绍。 以下是一个动态模块定义的示例 `DatabaseModule`: ```typescript 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, }; } } ``` > `forRoot()` 可以同步或异步(`Promise`)返回动态模块。 此模块 `Connection` 默认情况下(在 `@Module()` 装饰器元数据中)定义提供程序,但此外-根据传递给方法的 `entities` 和 `options` 对象 `forRoot()` -公开提供程序的集合,例如存储库。请注意,动态模块返回的属性扩展(而不是覆盖)`@Module()` 装饰器中定义的基本模块元数据。这就是从模块导出静态声明的 `Connection` 提供程序和动态生成的存储库提供程序的方式。 如果要在全局范围内注册动态模块,请将 `global` 属性设置为 `true`。 ```typescript { global: true, module: DatabaseModule, providers: providers, exports: providers, } ``` > 如上所述,将所有内容全局化不是一个好的设计决策。 可以通过以下`DatabaseModule`方式导入和配置: ```typescript 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 {} ``` 如果你想反过来重新导出一个动态模块,则可以 `forRoot()` 在导出数组中省略方法调用: ```typescript 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 {} ``` [动态模块](https://docs.nestjs.com/fundamentals/dynamic-modules)章介绍中更详细地在本主题,并且包括一个[实例](https://github.com/nestjs/nest/tree/master/sample/25-dynamic-modules)。