Nest.js 是一个高度模块化的框架,它鼓励使用模块(Module)来组织代码,以实现不同功能区块的隔离。
模块封装最佳建议:
- 单一职责原则:每个模块应该只关注一个功能和任务。
- 封装:模块内部的实现应该对外界隐藏,只通过 exports 露出必要的部分给外界使用。
- 配置与环境隔离:使用配置模块或服务来处理不同环境(开发、测试、生产)的配置。这有助于将配置管理从业务逻辑中解耦。
模块相互配合
导出 PersonService 以便其他模块使用:
导入 PersonModule 以使用它的 PersonService:
在 AppController 就可以使用导入的PersonMoudle 的 PersonService,以及自身 providers 数组的 AppService:
模块重导出
有时候我们希望在导出一个模块的同时,也重新导出它所导入的模块。这样可以让这个模块的消费者直接使用它依赖的模块的功能,而无需显式地导入它们。
在 Nest.js 中,可以使用 exports
数组来重导出模块:
typescript
// email.module.ts
@Module({
imports: [CommonModule],
providers: [EmailService],
exports: [EmailService, CommonModule] // 重导出 CommonModule
})
export class EmailModule {}
在上面的代码中,EmailModule
不仅导出了它自己的 EmailService
,也重导出了它导入的 CommonModule
。
这意味着导入 EmailModule
的模块也会自动导入 CommonModule
,并可以使用其提供的服务。
这种重导出机制在组织大型应用程序时非常有用,因为它可以减少模块间的耦合,同时简化外部模块的导入过程。
全局模块
全局模块 :AuthModule
被设置为全局模块,它的 AuthService
可以在应用中的任何地方使用,而无需显式导入 AuthModule
。
使用 @Global
声明成全局模块并 export 想导出的 Service。
typescript
import { Module } from '@nestjs/common';
import { AuthModule } from './auth/auth.module';
@Module({
imports: [AuthModule],
// ...其他配置
})
export class AppModule {}
一旦 AuthModule
被导入到根模块,它内部导出的模块就可以直接在其他模块使用,其他不用 imports 其 Module 便可使用其 service。
全局模块通常用于那些提供跨应用程序共享的基础服务的功能,例如配置服务、数据库服务或任何其他需要在多个模块之间共享的服务。
注意:避免过度使用全局模块,代码可读性更好。
动态模块
动态模块允许在模块被导入时动态配置提供者(providers)、控制器(controllers)和导出(exports)。
这种方式非常适合需要根据不同环境或配置动态更改其行为的模块。
新建 Nest 项目:
bash
nest new dynamic-module -p npm
创建 CRUD 模块:
bash
nest g resource dynamicModule
此时导入的 DynamicModuleModule 内容都是固定:
我们需要导入的时候给其传入一些参数怎么办?
这样写:
多了一个 module
属性指向当前的模块类,即 DynamicModuleModule
。
我们给 DynamicModuleModule 加一个 register 的静态方法,返回模块定义的对象,外部传入的 options 参数对象会作为一个新的 provider。
导入时配置:
使用 @Inject 注入依赖:
访问 localhost:3000,控制台打印:
这就是动态模块。
上面静态方法 register 方法可以任意取名,但 nest 约定了一些方法名:
register
:用一次模块传一次配置forRoot
:根模块(AppModule
)配置一次,例如数据库连接、核心服务等。forRootAsync
:类似于 forRoot,但用于异步配置,允许从异步服务中获取配置信息。它通常接收一个 useFactory 这个返回配置对象的工厂函数。forFeature
:用 forRoot 固定整体模块,用于局部的时候,可能需要再传一些配置。比如用 forRoot 指定了数据库链接信息,再用 forFeature 指定某个模块访问哪个数据库和表。forFeatureAsync
: 类似于 forFeature,但用于异步配置特定功能。
异步动态模块
如果需要异步地提供配置,可以使用 forRootAsync
方法。
这个方法通常与工厂函数或类一起使用,以便异步地解析配置数据,然后再创建模块:
typescript
@Module({})
export class DynamicModule {
static forRootAsync(options: DynamicModuleAsyncOptions): DynamicModule {
return {
module: DynamicModule,
imports: options.imports || [],
providers: [
{
provide: 'CONFIG_OPTIONS',
useFactory: options.useFactory,
inject: options.inject || [],
},
DynamicService,
],
exports: [DynamicService],
};
}
}
在这个异步版本中,useFactory
是一个工厂函数,它可以异步地返回配置对象。
inject
属性是一个数组,它定义了工厂函数的依赖项,这些依赖项将被注入到工厂函数中。