在 NestJS 中,一切皆模块(Module)是其核心理念之一,本文将深入探讨 NestJS 中模块的原理,并通过实践代码来展示如何高效地使用模块。
【NestJS 编程艺术】3. 探索NestJS的高效开发:nest-cli的全面指南 - 掘金 (juejin.cn) 还不会应用 nest-cli的同学,可以参考上篇
模块(Module)解析
在 NestJS 中,模块(Module)是应用程序的基本构建块。每个模块都是一个独立的单元,负责管理自己的控制器(Controllers)、服务(Services)、提供者(Providers)和导入其他模块。这种模块化的设计使得代码结构清晰,便于维护和扩展。
模块的结构
一个基本的 NestJS 模块包含以下几个部分:
- 控制器(Controllers) :负责处理客户端请求,并将请求委托给服务层处理。
- 服务(Services) :包含业务逻辑,通常与数据库或其他外部服务进行交互。
- 提供者(Providers) :可以是服务、值(Value)或类(Class),用于在应用程序中提供特定的功能或数据。
- 导入(Imports) :指定当前模块所依赖的其他模块,以便使用这些模块中定义的控制器、服务和提供者。
实践代码
让我们通过一个简单的例子来实践模块的使用。在 NestJS 中创建模块非常简单。你可以使用 CLI 工具生成模块的骨架,或者手动创建模块文件。
步骤 1: 创建模块
首先,我们创建一个名为 UserModule
的模块。以下为命令行创建方式:
sql
nest g module user
以下为生成的 user.module.ts
文件:
kotlin
// user.module.ts
@Module({
providers: [UserService],
controllers: [UserController],
})
export class UserModule {}
命令行的好处是可以自动将 UserModule 加入 app.module.ts
的 imports
数组中,如下:
less
// app.module.ts
@Module({
imports: [UserModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule { }
步骤 2: 定义控制器
控制器负责处理客户端请求,并将请求委托给服务层(步骤3要创建的UserService
)处理。 以下为命令行生成一个用户控制器 UserController
。
sql
nest g controller user
生成后的代码如下:
less
// user.controller.ts
@Controller('users')
export class UserController {
constructor(private readonly userService: UserService) {}
@Get()
findAll() {
return this.userService.findAllUsers();
}
}
步骤 3: 定义服务
通常我们的业务逻辑都写在service层,进行数据库增删改查的逻辑操作或其他外部服务进行交互。 然后,我们创建一个 UserService
服务,它将包含业务逻辑。
sql
nest g service user
kotlin
// user.service.ts
@Injectable()
export class UserService {
findAllUsers(): string[] {
// 这里可以是数据库查询或其他业务逻辑,可以通过 ORM 工具进行查询数据库的操作,并返回 User 列表;
// 下一篇会介绍 TypeORM 的接入
return ['User1', 'User2', 'User3'];
}
}
以上为 Module
的概览;NestJS 中的模块概念还包括全局模块
、共享模块
和动态模块
,这些模块类型提供了更高级的模块化和代码复用策略。下面我们将对这些概念进行补充说明。
全局模块
全局模块是一种特殊的模块,它的作用域是全局的。这意味着在全局模块中定义的控制器、服务和提供者可以被应用程序中的任何其他模块访问,而无需显式地导入它们。全局模块通常用于那些在多个地方被频繁使用的功能。
创建一个全局模块可以通过在 @Module()
装饰器中使用 global: true
选项来实现。
kotlin
// user.module.ts
@Module({
global: true,
providers: [UserService],
})
export class UserModule {}
在这个例子中,UserService
将在整个应用程序中可用,无需在每个使用它的模块中导入 UserModule
。 比如有一个 OrderModule
需要用到 UserService 提供的能力,就可以很方面的进行使用:
less
@Controller()
export class OrderController {
constructor(private readonly userService: UserService) {}
@Get()
getHello(): string {
return this.userService.getUsers();
}
}
共享模块
共享模块是一种可以在多个子模块中共享的模块。共享模块的目的是为了避免模块间的重复导入,从而减少模块间的耦合。当你有一个模块被多个其他模块共同依赖时,可以将这个模块设置为共享模块。
共享模块的创建与普通模块相同,但需要在导入时使用 SharedModule
。
kotlin
// shared.module.ts
@Module({
imports: [DatabaseModule],
exports: [DatabaseModule],
})
export class SharedModule {}
在这个例子中,DatabaseModule
被导入到 SharedModule
并被导出,这样任何导入 SharedModule
的模块都会间接导入 DatabaseModule
。
动态模块
动态模块允许你在运行时动态地加载和卸载模块。这种模块特别适用于需要根据应用程序状态或用户请求来改变应用程序行为的场景。动态模块可以通过 DynamicModule
装饰器来创建。
kotlin
// dynamic.module.ts
@Module({
// 动态加载的配置项可以在这里定义
})
export class DynamicModule {}
动态模块通常与 forRoot
或 forFeature
方法结合使用,这些方法可以在应用程序的不同生命周期阶段加载模块。
kotlin
// app.module.ts
import { DynamicModule } from '@nestjs/common';
@Module({
imports: [
SomeModule.forRootAsync({
useFactory: () => new DynamicModule(),
}),
],
})
export class AppModule {}
在这个例子中,SomeModule
使用 forRootAsync
方法异步加载 DynamicModule
。
总结
全局模块、共享模块和动态模块为 NestJS 应用程序提供了更灵活的模块化策略。全局模块适用于全局共享的服务和控制器,共享模块用于减少模块间的耦合,而动态模块则允许在运行时动态地加载和卸载模块。这些高级模块类型可以帮助你构建更加灵活和可维护的 NestJS 应用程序。