前言
近几年,由于 Node.js 的广泛流行,JavaScript 已经成为前端和后端应用程序的「通用语言」,从而产生了像Angular、React、Vue等令人耳目一新的项目,这些项目提高了开发人员的生产力,使得可以快速构建可测试的且可扩展的前端应用程序。然而,在服务器端,虽然有很多优秀的库、helper 和 Node 工具,但是它们都没有有效地解决主要问题 - 架构。
Nest 旨在提供一个开箱即用的应用程序体系结构,允许轻松创建高度可测试、可扩展、松耦合且易于维护的应用程序。
Nest 是用于构建高效且可扩展的服务器端应用程序的渐进式 Node.js 框架,深受 Spring 和 Angular 的启发。它兼容 TypeScript 和纯 JavaScript,并结合了 OOP(面向对象编程)、FP(函数式编程)和 FRP(函数响应式编程),底层默认使用 Express,但也提供了与其他各种库的兼容(例如 Fastify),可以方便地使用各种可用的第三方插件。
Controllers
Controllers
负责接收客户端的请求并返回响应。
Controller
的目的是接收应用的特定请求,路由机制控制哪个控制器接收哪些请求。通常,每个控制器有多个路由,不同的路由可以执行不同的操作。为了创建一个基本的控制器,我们使用类和装饰器。装饰器将类与所需的元数据相关联,使得 Nest
可以创建路由映射(将请求绑定到相应的控制器)。
1 | /** |
Providers
Provider
只是一个用 @Injectable()
装饰器注释的纯粹的 JavaScript
类。
Provider
的主要目的是它可以注入依赖项,这意味着对象可以彼此创建各种关系,并且“连接”对象实例的操作在很大程度上可以委托给 Nest
运行时系统。使用 Nest
内置的依赖注入(DI)技术,通过构造函数参数的方式,可以将 Provider
注入到其他类中。
当注入 Provider
时,它必须要在注入的类的模块范围内可见,这可以通过以下几种方式来完成:
- 在同一模块范围内定义
provider
- 从一个模块作用域导出
provider
并将该模块导入到要注入的类的模块作用域中 - 从标记了
@Global()
装饰器的全局模块中导出provider
在前面的示例中,控制器接收客户端 HTTP 请求并将复杂的业务处理委托给 Provider
来完成:
1 | /** |
Dependency injection
依赖,是当类需要执行其功能时,所需要的服务或对象; DI 是一种编码模式,其中的类会从外部源中请求获取依赖,而不是自己创建它们。依赖注入(DI)是一种控制反转(IOC)技术,您可以将依赖的实例化操作委派给IOC容器(Nest
运行时系统)来代替自己在代码中进行实例化操作。
在 Nest
中,借助于 TypeScript 的特性,管理依赖项非常容易,因为仅需按类型进行解析即可。
Scopes
Providers
通常具有与应用程序一致的生命周期。当应用程序启动时,必须处理解决每个依赖项,因此必须实例化每个 Provider
;同样,当应用程序关闭时,每个 Provider
都将被销毁。
Custom providers
Nest
有一个内置的 IOC 容器(Nest
运行时系统),可以解决 Providers
之间的依赖关系。此功能是上面描述的依赖注入功能的基础,但实际上要比我们到目前为止描述的功能强大得多。
@Injectable()
装饰器并不是定义一个 Provider 的唯一方式,我们还可以使用Value、Class、Factory来定义一个 Provider。
1 | ({ |
Property-based injection
除了基于构造函数注入 providers
的方式之外,在某些非常特殊的情况下,基于属性的注入可能会很有用。例如,如果父类依赖于一个或多个 providers,那么通过从构造函数中调用子类中的 super()
来传递它们就会非常烦人了。因此,为了避免出现这种情况,可以在属性上使用 @Inject()
装饰器。
Provider registration
现在我们已经定义了一个提供者(DictService
),并且也已经有了该服务的使用者(DictController
),此外,我们还需要将该服务注册到 Nest 中以便它可以进行注入。因此,我们需要编写模块文件(DictModule
),并将该服务添加到 @Module()
装饰器的 providers
数组中:
1 | ({ |
在上面的示例中,当 Nest IOC
容器实例化 DictController
时,它首先查找所有的依赖项;当找到 DictService
依赖项时,它将对 DictService
令牌执行查找,根据 DictModule
的配置,将返回 DictService
类;然后,Nest 创建 DictService
实例,将其缓存并返回,或者如果实例已经在缓存中存在,则返回现有实例。
Modules
模块是用 @Module()
装饰器注释的类。Nest 使用模块来组织应用程序结构,@Module()
装饰器提供了 Nest 用来组织应用程序结构的元数据。
Metadata | Describe |
---|---|
providers | 由 Nest 注入器实例化的providers ,并且至少可以在此模块中共享 |
controllers | 在此模块中定义的需实例化的一组controllers |
imports | 导入模块的列表,这些模块道导出了此模块中所需的providers |
exports | 由本模块提供供其他模块导入使用的providers 子集 |
默认情况下,模块封装提供者,这意味着如果提供者既不是当前模块的一部分也不是从另外模块(已导入)导出的,那么它是无法注入的。
Middleware
Middleware
是在路由处理程序之前执行的函数,该函数可以访问 request 和 response 对象,以及应用程序请求-响应周期中 next()
中间件函数。Nest 中间件实际上等价于 express 中间件。
中间件无法在这 @Module()
装饰器中进行配置,我们可以通过使用模块类的 configure()
方法来进行设置,且包含中间件的模块必须实现 NestModule
接口。