内容提要
本文介绍 NestJS 基础,核心是 IOC 控制反转与 DI 依赖注入,遵循依赖倒置原则解耦代码。内容涵盖装饰器、CLI 命令、项目结构、控制器路由与参数装饰器、RESTful 风格、版本控制、Code 码规范、Provider 注册与作用域、模块与动态模块、中间件,并对比 Express 说明其架构优势。
延伸解读
从 Express 到 NestJS:架构约束的价值
文章指出 Express 虽然灵活,但缺乏架构约束,项目变大后依赖关系容易混乱。NestJS 通过 IOC 和 DI 强制解耦,并引入模块化 AOP 架构,使代码组织更清晰。对于需要长期维护和扩展的 Node.js 后端项目,这种约束能降低维护成本,但也会带来一定的学习曲线。
依赖注入:解耦的关键实践
文章用电脑的例子说明,传统方式在类内部直接实例化依赖会导致耦合,违反依赖倒置原则。NestJS 的 DI 容器通过构造函数、方法参数或属性注入依赖,对象被动接受依赖,从而解耦。这使替换实现(如从 WinComputer 换到 LinuxComputer)只需修改注册,无需改动使用方。
作用域选择:单例与请求作用域的权衡
NestJS 提供默认(单例)、请求和瞬态三种作用域。单例在整个应用共享实例,性能最佳;请求作用域为每个请求创建新实例,适合需要请求上下文的场景,但会动态创建实例,影响响应时间。文章强调若非必要应保持单例,避免不必要的性能开销。
模块共享与动态模块:组织大型应用
模块通过 imports 和 exports 共享提供者,避免重复注册导致多实例。动态模块允许传入参数动态配置,如 ConfigModule.forRoot(),并支持全局模块减少模板代码。但文章也提醒,使一切全局化并非好方案,imports 数组仍是保持模块 API 透明的最佳方式。
Q&A
NestJS 中的控制反转(IOC)和依赖注入(DI)是什么?它们如何帮助解耦代码?
控制反转(IOC)是一种设计模式,将程序流程和资源的控制权从对象内部转移到外部容器(IOC 容器),以解耦代码。依赖注入(DI)是实现 IOC 的一种方式,通过容器动态管理对象的创建和依赖关系,将依赖对象通过构造函数、方法参数或属性注入,而不是在对象内部直接创建。这样,高层模块不直接依赖低层模块,而是依赖抽象,符合依赖倒置原则,使代码更易维护和扩展。
NestJS 项目的基本目录结构是怎样的?各个文件有什么作用?
基本结构包括:src 目录下的 app.controller.ts(处理 HTTP 请求和路由)、app.service.ts(业务逻辑)、app.module.ts(根模块,组织模块和依赖注入)、main.ts(入口文件,创建应用实例并监听端口),以及 nest-cli.json(CLI 配置文件)。此外,还有测试文件如 app.controller.spec.ts。常见后缀如 .middleware.ts、.controller.ts、.service.ts 等分别表示中间件、控制器、服务等。
如何在 NestJS 中实现 RESTful API?请举例说明。
RESTful 是一种风格,将一切视为资源,使用 HTTP 动词(GET、POST、PUT、DELETE 等)操作资源,同一资源使用同一 URL。在 NestJS 中,使用 @Controller 装饰器定义路由前缀,结合 @Get、@Post、@Patch、@Delete 等装饰器处理不同请求。例如:@Controller('list') 定义前缀,@Get() 查询所有,@Get(':id') 查询单个,@Post() 创建,@Patch(':id') 更新,@Delete(':id') 删除。
NestJS 中如何注册和使用 Provider?有哪些不同的提供者类型?
Provider 是带有 @Injectable() 装饰器的类,用于提供业务逻辑。在模块的 providers 数组中注册。类型包括:类提供者(useClass)、值提供者(useValue)、工厂提供者(useFactory)、别名提供者(useExisting)。默认按类型注入,若自定义名称需使用 @Inject(token) 显式注入。Provider 可以注入到控制器、其他提供者等。
NestJS 的模块是什么?如何实现模块共享和全局模块?
模块是带有 @Module() 装饰器的类,用于组织应用结构。@Module 元数据包括 providers、controllers、imports、exports。模块共享:在 imports 中导入其他模块,并使用其 exports 的提供者,这样共享同一实例,符合单例模式。全局模块:使用 @Global() 装饰器,并在根模块中导入一次,其他模块无需导入即可使用其提供者。
NestJS 中间件如何使用?如何配置路由中间件和全局中间件?
中间件是在路由处理程序之前调用的函数,可以操作请求和响应,通过 next() 传递控制权。中间件可以是函数或实现了 NestMiddleware 接口的类。路由中间件:在模块中实现 NestModule 接口的 configure 方法,使用 consumer.apply(中间件).forRoutes(路由) 配置,可用 exclude 排除路由。全局中间件:在 main.ts 中使用 app.use(中间件) 注册。