什么是模块?🤔
模块(Module)是 NestJS 中组织应用程序结构的基本单位。每个 NestJS 应用至少有一个根模块(通常是 AppModule)。模块可以帮助我们将相关的功能组织在一起,使代码结构更清晰。
Express vs NestJS 对比 🆚
Express 写法:
javascript
// 通常需要手动组织文件结构
project/
├── routes/
│ ├── users.js
│ └── auth.js
├── services/
│ ├── users.js
│ └── auth.js
├── middlewares/
└── app.js
// app.js
const express = require('express');
const usersRouter = require('./routes/users');
const authRouter = require('./routes/auth');
const app = express();
app.use('/users', usersRouter);
app.use('/auth', authRouter);NestJS 写法:
typescript
// 使用模块自动组织代码
@Module({
imports: [UsersModule, AuthModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}模块装饰器详解 🎯
基本模块结构
typescript
@Module({
imports: [], // 👈 导入其他模块
controllers: [], // 👈 模块的控制器
providers: [], // 👈 模块的提供者
exports: [] // 👈 导出给其他模块使用的提供者
})
export class FeatureModule {}模块的类型 🌟
1. 功能模块
typescript
// users/users.module.ts
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UsersController],
providers: [UsersService],
exports: [UsersService] // 👈 导出服务供其他模块使用
})
export class UsersModule {}2. 共享模块 🤝
typescript
// shared/shared.module.ts
@Module({
imports: [ConfigModule],
providers: [
LoggerService,
{
provide: 'API_KEY',
useValue: process.env.API_KEY
}
],
exports: [LoggerService, 'API_KEY'] // 👈 导出多个提供者
})
export class SharedModule {}3. 动态模块 🔄
typescript
// config/config.module.ts
@Module({})
export class ConfigModule {
static forRoot(options: ConfigOptions): DynamicModule {
return {
module: ConfigModule,
providers: [
{
provide: 'CONFIG_OPTIONS',
useValue: options,
},
ConfigService,
],
exports: [ConfigService],
};
}
}
// 使用动态模块
@Module({
imports: [
ConfigModule.forRoot({
folder: './config',
encoding: 'utf8'
})
]
})
export class AppModule {}4. 全局模块 🌍
typescript
@Global() // 👈 声明为全局模块
@Module({
providers: [GlobalService],
exports: [GlobalService],
})
export class GlobalModule {}模块注册方式 📝
1. 静态注册
typescript
@Module({
imports: [UsersModule, AuthModule, SharedModule],
})
export class AppModule {}2. 异步注册
typescript
@Module({
imports: [
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
type: 'mysql',
host: configService.get('DB_HOST'),
port: configService.get('DB_PORT'),
username: configService.get('DB_USER'),
password: configService.get('DB_PASSWORD'),
database: configService.get('DB_NAME'),
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: true,
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}实战示例 💡
让我们创建一个完整的博客系统模块结构:
typescript
// blog/blog.module.ts
@Module({
imports: [
TypeOrmModule.forFeature([Post, Comment]),
UsersModule, // 👈 导入用户模块
SharedModule // 👈 导入共享模块
],
controllers: [
PostsController,
CommentsController
],
providers: [
PostsService,
CommentsService,
{
provide: 'CACHE_MANAGER',
useFactory: (config: ConfigService) => {
return new CacheManager(config.get('CACHE_TTL'));
},
inject: [ConfigService]
}
],
exports: [PostsService] // 👈 导出帖子服务
})
export class BlogModule {}
// app.module.ts
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true, // 👈 全局配置
envFilePath: `.env.${process.env.NODE_ENV}`
}),
TypeOrmModule.forRoot({
// 数据库配置
}),
BlogModule,
UsersModule,
AuthModule
],
controllers: [AppController],
providers: [AppService]
})
export class AppModule {}模块组织最佳实践 ✨
1. 按功能组织模块
typescript
// 推荐的项目结构
src/
├── users/
│ ├── dto/
│ ├── entities/
│ ├── users.controller.ts
│ ├── users.service.ts
│ └── users.module.ts
├── auth/
│ ├── guards/
│ ├── auth.controller.ts
│ ├── auth.service.ts
│ └── auth.module.ts
├── shared/
│ ├── services/
│ ├── constants/
│ └── shared.module.ts
└── app.module.ts2. 合理使用共享模块
typescript
// shared/shared.module.ts
@Module({
imports: [ConfigModule],
providers: [
LoggerService,
UtilsService,
{
provide: 'API_CLIENT',
useFactory: (config: ConfigService) => {
return new ApiClient(config.get('API_URL'));
},
inject: [ConfigService]
}
],
exports: [LoggerService, UtilsService, 'API_CLIENT']
})
export class SharedModule {}3. 使用动态模块进行配置
typescript
// database/database.module.ts
@Module({})
export class DatabaseModule {
static forRoot(options: DatabaseOptions): DynamicModule {
return {
module: DatabaseModule,
providers: [
{
provide: 'DATABASE_OPTIONS',
useValue: options
},
DatabaseService
],
exports: [DatabaseService]
};
}
}
// 使用方式
@Module({
imports: [
DatabaseModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306
})
]
})
export class AppModule {}4. 避免循环依赖
typescript
// ❌ 不好的做法:循环依赖
// users.module.ts
@Module({
imports: [PostsModule],
})
export class UsersModule {}
// posts.module.ts
@Module({
imports: [UsersModule], // 循环依赖!
})
export class PostsModule {}
// ✅ 好的做法:通过共享模块解决
@Module({
imports: [SharedModule],
})
export class UsersModule {}
@Module({
imports: [SharedModule],
})
export class PostsModule {}模块生命周期 🔄
NestJS 模块也有生命周期钩子:
typescript
@Module({})
export class FeatureModule implements OnModuleInit, OnModuleDestroy {
onModuleInit() {
// 模块初始化时执行
console.log('Module has been initialized');
}
onModuleDestroy() {
// 模块销毁时执行
console.log('Module is being destroyed');
}
}总结 📝
通过这篇文章,我们学习了:
- 模块的基本概念和类型
- 不同的模块注册方式
- 如何组织和结构化模块
- 模块的最佳实践
- 模块的生命周期
模块是 NestJS 应用程序的基石,合理使用模块可以让我们的应用更加模块化和可维护。
下一篇文章,我们将探讨 NestJS 的中间件(Middleware)系统。