NestJS 探索与实践
前段时间,我司前端基建团队上线了重定向管理系统——它提供二维码、短链接的创建和维护服务。
这是由前端团队全栈开发的项目:前端使用 React,后端使用 Nestjs 开发。我们将开发过程中得到的一些实践经验和思考,借着这个机会想和大家共同探讨一下。也希望通过这次分享,让大家对 NestJS 有一个初步的了解。
主题这次分享主要包含4个内容
-
什么是 nestJS?
-
应用场景和能力,能解决什么问题?
-
为什么选择它,有什么优势?
-
如何上手?
What
我们先回答第一个问题:什么是 nestJS。图中是官网的一段定义,大意是:
Nest 是一个用于构高效,可扩展的 Node.js 服务端应用程序的框架…… 它使用渐进式 JavaScript,内置并完全支持 TypeScript
从定义中,我们找出几个关键字来看看:
-
基于 Node:对前端友好
-
服务端应用框架:主要用于服务端接口开发
-
高效,可扩展:体现在 Nest 各个功能模块之间的架构是解耦的、容易进行组合的
-
渐进式:不需要一开始掌握它的全部功能特性,后续可根据业务需要逐步增加功能。
-
Nest 由 TS 开发,完全支持 TS
简单来说,Nest 是一个具有诸多特性的 Node 服务端框架。
When
第二个问题:有哪些应用场景和能力?能解决什么问题?
-
首先是最基础的,做服务端开发;
-
其次,对服务端功能的扩展,比如:安全、鉴权、队列、日志等
-
技术架构级支持:微服务,序列化等等
以上这些场景,官方都提供成熟的解决方案,对技术选型也是一种可靠的保障。
Why
说完 Nest 的应用场景,我们再来谈谈:为什么选择 Nest?我们简单回顾一下 Node Web 框架的发展历程,我这里将它们分为 起步和规模化 两个阶段:
起步阶段 从 09 年 Node.js诞生开始,紧接着出现 Express,Koa。它们主打轻量、极简的框架
-
它们开发风格自由开放,导致的结果是:大家都有自己的一套开发方式(不同的分层,项目结构,文件命名)。
-
框架功能过于专一甚至简单,团队项目很少直接使用它们开发;它们的特点好像 web 中的 JQuery,依然强大,但已经不能满足复杂的开发需求
在这样的背景下,诞生了 Egg 和 Nest——主打企业级应用和团队协作,开箱即用
Nest 与 Egg 对比
Express 和 Koa 功能比较基础,Nest 与 Egg 是我们主要考虑的两种方案,我们通过以下几个方面来进行比较:
-
社区生态:Nest 社区非常繁荣,由官方提供解决方案;Egg 拥有插件市集,但插件质量参差不齐
-
关注度,Nest 在 GitHub 上有 4.2 万 star,Egg 是 1.8 万(截止2022年1月);
-
项目更新频率:Nest 高一些,github issue 反馈及时,使用体验更好
-
架构设计:代码组织方式更合理——项目结构按照功能模块划分;装饰器语法更加优雅;基于依赖注入实现代码低耦合;Egg 提倡约定大于配置,缺乏一定的灵活性
-
Nest 原生支持 TS
-
上手难度:Nest 概念较多,难度稍高一点
-
维护成本,Nest 有一套标准化的开发流程,长期来看,利于保持项目的统一性
综合以上几个方面,我们认为:Nest 设计更加合理,也更适合我们团队
去年 12月, 阿里的 D2 大会上,也推出新的 Node Web 框架:MidWay。整体设计和 Nest 比较类似:支持装饰器,基于 DI 设计,支持 TS —— Egg.js 似乎完成了他的历史使命,将接力棒交到了 Midway 的手中
How
接下来我们从代码层面,介绍如何上手 Nest
脚手架
-
Nest 官方提供了脚手架,可通过 npm 全局安装,使用方法与 Vue client 大同小异,开发体验是比较不错的
-
使用 Nest CLI 建立新项目非常简单,通过
nest new xxx
一键创建,会生成样板代码、安装依赖 -
使用命令
npm start:dev
启动应用程序,监听入站 HTTP 请求。
目录结构
初始化的项目包含一些样板文件,主要看一下 src 目录,里面包含了几个核心文件
main.ts
main.ts 是程序的入口文件,它包含一个异步函数:负责创建 app 实例,启动、监听服务器。内部引入了 app.module —— 模块文件,这是 Nest 一个核心概念
基础概念
module
-
中文名叫模块,在 Nest 中是可运行功能的最小单元。
-
举个例子,我们的应用中有 用户管理,订单管理 等多个功能,每个功能都可独立成一个模块,下面这幅图,虚线表示服务端应用,其中用户管理功能是 UserModule,订单管理是 OrderModule,应用则是由许多的模块 moudule 组成……在他们前面还有一个 AppModule,是整个应用根模块。
-
整个模块的结构类似前端 SPA 应用:前端应用 app 挂载到 root 根节点上,pages 目录下的文件映射为不同的页面组件
-
为了创建一个基本的模块,我们将使用 类 和 装饰器
装饰器
-
装饰器是一种特殊的语法,它用来修改或增强类、函数、对象等,写法是
@expression
, 在 TS 中率先支持这种语法 -
expression
表达式求值后必须为一个函数,它会在运行时被调用;被装饰的主体做为参数传入。 -
代码中在 AppModule 这个类前面,添加了一个
@Module()
装饰器,作用是返回一个模块类,并提供模块的元数据(上下文及依赖)。
app.module.ts 中引入了 Controller 和 Service ,我顺着代码的依赖关系,来介绍一下 Controller
controller
-
控制器,负责处理传入的请求和返回的响应。
-
它像一个处理器(dispatcher),接收 HTTP 请求,然后通过 路由分发 机制,调用命中的方法
-
@Get
这个请求类型装饰器,可以接受一个路由地址。如果路由命中,则会调用getHello
方法;这里为空,会匹配根路径 -
控制器重要负责处理
HTTP
请求,而将更复杂的任务委托给提供者,即 Service(providers), Service 是什么?
Service
-
在 Nest 中,服务 是一种常见的 提供者(Provider) ,它负责一些基础、公用的方法,比如处理业务逻辑,与数据库交互……提供者通过依赖注入的方式,被注入到控制器(Controller)中,这些方法由 Controller 调用。
刚刚介绍了Nest 中最核心的三个概念
-
模块是 Nest 中的最小单元,许许多多的单元组成了应用;也可以看作一个容器,如下图,每个模块中包含了控制器和提供者
-
控制器处理
HTTP
请求,分发路由;调用提供者的方法; -
提供者中定义了基础/公共的逻辑
需要注意的是:
-
在开发中为了避免把逻辑分散在各个文件中,要注意区分控制器和提供者边界,
即:
-
在 Controller 中主要处理请求、分发路由;注意,一个控制器中可以调用多个提供者;
-
在提供者中处理基础业务逻辑
如下图代码…这样进行功能分层,能够保障代码的清晰和统一
除此之外,Nest 还有中间件(在路由程序前后执行),异常过滤器(处理程序抛出的异常错误),守卫(权限校验)……等概念,用于开发一些常见的功能。但我没有把它们归类到 Nest 核心概念中,大家有兴趣下来可以再了解。
依赖注入
前面经常讲到 Nest 是可扩展,低耦合的,那么它是怎么实现的呢?答案是:依赖注入
依赖注入(Dependecy Injection)是实现低耦合的一种方式(也可以叫设计模式),它将对象创建和对象消耗分开。所需的依赖关系不是在内部创建,而是通过外部透明地传递
这里有一段代码,包含两个类:第一个是 引擎 Engine
,第二个是 汽车 Car
-
Engine 有一个 cylinders 属性,表示引擎类型,我先给他一个固定属性为内燃机引擎
-
Car 构造器中通过 new 实例化了一个引擎,在 drive 方法中使用了引擎的类属性,返回字符串:这是一个内燃机引擎汽车
-
有一天,我打算改动 Engine,不再设置固定类型,而是通过构造器动态传入;那么,Car 也需要改动构造函数中的代码。这样一来就不符合低耦合的标准。
那我们使用依赖注入的方式,该怎么处理?
-
在汽车中,通过构造函数直接传递 Engine 对象,而不是在内部创建
-
在外部 main 方法中,实例化 Car 时,也会实例化 Engine 对象,并把 Engine 传入到 Car 中;这个过程叫做注入;注意,这里是把 Engine 传入到 Car 中;
-
这样修改后,无论 Engine 的逻辑怎样改变,都不会影响到 Car 的代码
我们再回头看一下,Nest 中是怎么应用依赖注入的?
-
回到 Module 文件,
@Module
装饰器中会声明 controllers 和 providers。@Module
装饰器实际是替代了 main 函数。会实例化 Controller 和 Provider,并将 Provider 注入到 Controller 中
-
于是在 Controller 中可以通过 this 调用 service。
Nest 中是将 provider 注入到 controller,因此 Provider 的装饰器名字叫做 Injectable
这就是在 Nest 从框架层面,通过依赖注入实现模块的低耦合,从而提高了代码的可扩展性。
Summary通过上面对 Nest 的讲解,简单总结一下:
第一:我们主要从四个方面来介绍了 Nest:What,When,Why,How
第二:Nest 存在一些缺点:它的概念较多;设计模式与平常不一样;国内开发者目前比较少;
总体评价 Nest.js,我觉得它是一个「优雅的,标准化,可扩展的」 Node 框架。在使用过程中除了知道这玩意怎么用,也能慢慢学习到后端开发模式,Nest 在的设计上的优点也是值得我们学习和探索的。
作者:samwangdd
链接:https://juejin.cn/post/7054931414478749710
来源:稀土掘金