前言
近几年,由于 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 接口。