Skip to content

什么是模块?🤔 ​

模块(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.ts

2. 合理使用共享模块 ​

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');
  }
}

总结 📝 ​

通过这篇文章,我们学习了:

  1. 模块的基本概念和类型
  2. 不同的模块注册方式
  3. 如何组织和结构化模块
  4. 模块的最佳实践
  5. 模块的生命周期

模块是 NestJS 应用程序的基石,合理使用模块可以让我们的应用更加模块化和可维护。

下一篇文章,我们将探讨 NestJS 的中间件(Middleware)系统。

相关资源 📚 ​