在 NestJS 中,动态模块允许在运行时动态添加和删除模块。这对于创建可扩展的和灵活的应用程序非常有用。
新建一个项目:
nest new dynamic-module -p npm
创建一个crud的模块:
nest g resource test
启动项目 浏览器访问 可以发现模块生效了
pnpm run start:dev
但是这个模块目前是静态的 内容固定不变 当我们想在mport 的时候给这个模块传一些参数,动态生成模块的内容 就可以使用 Dynamic Module动态模块
修改 test.module.ts: 给 TestModule 加一个 register 的静态方法,返回模块定义的对象
import { DynamicModule, Module } from '@nestjs/common';
import { TestService } from './test.service';
import { TestController } from './test.controller';
@Module({
})
export class TestModule {
static register(options: Record<string, any>): DynamicModule {
return {
module: TestModule,
providers: [
{
provide: 'TEST_OPTIONS',
useValue: options
},
TestService
],
exports: [TestService]
}
}
}
接着在 app.module.ts 使用
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TestModule } from './test/test.module';
@Module({
imports: [TestModule.register({ test: '666' })],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
最后在 test.controller.ts打印
constructor(private readonly testService: TestService,
@Inject('TEST_OPTIONS') private configOptions: Record<string, any>) { }
@Get()
findAll() {
console.log(this.configOptions);
return this.testService.findAll();
}
启动项目
pnpm run start:dev
访问 http://localhost:3000/test 可以看到控制台打印了 config 对象信息
这样我们就可以在 import 一个模块的时候,传入参数,然后动态生成模块的内容。
nest 约定了 3 种方法名:
foregister 是用于在整个应用程序中注册全局提供者的方法。当我们在应用程序的任何地方都需要访问该提供者时,可以使用 register 来注册。
forRoot 是用于在根模块中注册模块的方法。它将模块中的提供者和导出的模块注册为全局可访问的,这意味着其他模块可以直接使用这些提供者和导出的模块。
forFeature 是用于在特定模块中注册模块的方法。它将模块中的提供者和导出的模块注册为仅在该模块中可访问的。这通常用于在模块内部使用的服务和功能。
并且这些方法都可以写 xxxAsync 版本,也就是传入 useFactory 等 option,内部注册异步 provider。
Nest 还提供了另一种方式来创建动态模块
我们在生成一个新的模块:
nest g module aaa
创建一个controller:
nest g controller aaa --no-spec
新建一个 aaa.module-definition.ts 文件:
import { ConfigurableModuleBuilder } from "@nestjs/common";
/**
* 定义了AaaModule的配置选项接口。
*
* 该接口包括了aaa和bbb两个属性,分别对应不同的配置需求。
*/
export interface AaaModuleOptions {
aaa: string;
bbb: number;
}
/**
* 使用ConfigurableModuleBuilder构建AaaModule的配置类和选项令牌。
*
* 通过这种方式,可以动态配置模块的选项,并提供了一个令牌用于依赖注入,以便在模块中访问这些配置选项。
*/
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
new ConfigurableModuleBuilder<AaaModuleOptions>().build()
这段代码是使用NestJS框架定义了一个可配置的模块AaaModule。
- AaaModuleOptions接口定义了模块的配置选项,包括aaa属性(类型为字符串)和bbb属性(类型为数字)。
- ConfigurableModuleBuilder是一个NestJS提供的类,用于构建可配置模块。通过调用其build()方法,可以生成一个配置类和一个选项令牌。
- ConfigurableModuleClass是生成的配置类,可以用于动态配置模块的选项。
- MODULE_OPTIONS_TOKEN是生成的选项令牌,可以用于依赖注入,在模块中访问配置选项。
然后我们在AaaModule 里继承它
import { Module } from '@nestjs/common';
import { AaaController } from './aaa.controller';
import { ConfigurableModuleClass } from './aaa.module-definition';
@Module({
controllers: [AaaController]
})
export class AaaModule extends ConfigurableModuleClass {}
这样这个 CccModule 就已经有了 register 和 registerAsync 方法
app.module.ts:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TestModule } from './test/test.module';
import { AaaModule } from './aaa/aaa.module';
@Module({
imports: [TestModule.register({ test: '666' }),
AaaModule.register({
aaa: 'aaa',
bbb: 1
}),
// AaaModule.registerAsync({
// useFactory: () => ({
// aaa: 'aaa',
// bbb: 1
// })
// })
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
接着我们在 Controller 注入 aaa.controller.ts
import { Controller, Get, Inject } from '@nestjs/common';
import { AaaModuleOptions, MODULE_OPTIONS_TOKEN } from './aaa.module-definition';
@Controller('aaa')
export class AaaController {
@Inject(MODULE_OPTIONS_TOKEN)
private options: AaaModuleOptions;
@Get('')
hello() {
return this.options;
}
}
启动项目
pnpm run start:dev
可以看到拿到了options 对象 我们可以用来做配置
如果要用 forRoot、forFeature 可以调用 setClassMethodName 函数来设置一下
import { ConfigurableModuleBuilder } from "@nestjs/common";
/**
* 定义了AaaModule的配置选项接口。
*
* 该接口包括了aaa和bbb两个属性,分别对应不同的配置需求。
*/
export interface AaaModuleOptions {
aaa: string;
bbb: number;
}
/**
* 使用ConfigurableModuleBuilder构建AaaModule的配置类和选项令牌。
*
* 通过这种方式,可以动态配置模块的选项,并提供了一个令牌用于依赖注入,以便在模块中访问这些配置选项。
*/
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
new ConfigurableModuleBuilder<AaaModuleOptions>().setClassMethodName('forRoot').build()
import { ConfigurableModuleBuilder } from "@nestjs/common";
/**
* 定义了AaaModule的配置选项接口。
*
* 该接口包括了aaa和bbb两个属性,分别对应不同的配置需求。
*/
export interface AaaModuleOptions {
aaa: string;
bbb: number;
}
/**
* 使用ConfigurableModuleBuilder构建AaaModule的配置类和选项令牌。
*
* 通过这种方式,可以动态配置模块的选项,并提供了一个令牌用于依赖注入,以便在模块中访问这些配置选项。
*/
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
new ConfigurableModuleBuilder<AaaModuleOptions>().setClassMethodName('forFeature').build()
我们还可以设置为全局模块:
setExtras 第一个参数是给 options 扩展啥 extras 属性,第二个参数是收到 extras 属性之后如何修改模块定义
import { ConfigurableModuleBuilder } from "@nestjs/common";
/**
* 定义了AaaModule的配置选项接口。
*
* 该接口包括了aaa和bbb两个属性,分别对应不同的配置需求。
*/
export interface AaaModuleOptions {
aaa: string;
bbb: number;
}
/**
* 使用ConfigurableModuleBuilder构建AaaModule的配置类和选项令牌。
*
* 通过这种方式,可以动态配置模块的选项,并提供了一个令牌用于依赖注入,以便在模块中访问这些配置选项。
*/
export const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN } =
new ConfigurableModuleBuilder<AaaModuleOptions>()
.setClassMethodName('register')
.setExtras({
isGlobal: true
}, (definition, extras) => ({
...definition,
global: extras.isGlobal,
}))
.build()