背景
进入部门工作后,接触到的node.js服务端框架,是egg.js,后面基于扩展增加了很多插件,比如:@Controller @Service等注解,还有针对egg-framework 定制化部门使用的底层framework。
但是,随着时间的迁移,egg已经不太能满足我们的开发效率和开发模式,主要有以下几点:
- 对typescript支持度不够,这是由于egg.js本身就不是typescript开发
- egg.js封装web架构,约定大于编码,如:强制将web应用分级为: controller、service、middleware、extend等,自由度相对比较弱,当你需要定制化开发内容,你需要深入了解egg.js的整个运行原理才能实现
- 虽然部门内部定制化开发 @Controller @Service等注解,减少路由配置,但是这一块插件还存在一些隐藏规则,需要开发注意
当然egg.js运行的web应用还是比较稳定,而且相关插件生态也比较丰富,只是当egg.js迭代更新速度在2020年后就逐步放缓,更不上变化,我们就需要迎接一些新的框架来满足要求。
框架对比
我从近两年听到或者网上收集的,基于Node.js的框架主要有以下几个:
- 基础框架,基本上还是以express、koa、Fastify.js等为主
- egg.js,以MVC为架构的web框架
- nest.js,以Ioc 控制反转作为核心概念的web框架,对typescript支持友好
- nuxt.js,以Vue.js作为SSR服务端渲染核心的web框架,最新是Nuxt3(以Vue3为核心)
- next.js,以React.js作为SSR服务端渲染核心的
- Meteor.js,full-stack javascript平台,最大的特点是当数据发生改变的时候,所有依赖该数据的地方自动发生相应的改变。
- Fastify.js,号称最快的node.js web框架,特点是内置了基于 JSON schema 的 validation 和 serialization,比JSON.stringify还快的json序列化算法,虽然是借助借助第三方库 ajv。
- strapi.js,快速生成API接口的web框架,同时实现各种后端所需要的鉴权、权限、文件上传等轮子
对比一下,我们主要用来开发后端api接口,不需要SSR,不需要过于重或过于轻量的框架,因此最后挑选了nest.js。
nest.js
Nest (NestJS) 是一个用于构建高效、可扩展的Node.js服务器端应用程序的框架。它使用渐进式 JavaScript,构建并完全支持TypeScript(但仍然允许开发人员使用纯 JavaScript 进行编码)并结合了 OOP(面向对象编程)、FP(函数式编程)和 FRP(函数式响应式编程)的元素。
术语介绍:
- 什么是渐进式?简单说,就一开始你不需要了解它的全部功能,能快速上手,有些功能特性不用也可以正常使用。
- OOP 面向对象编程,万物皆可用对象来描述,如:
class Dog{ say(return 'one one!')}
- FP 函数式编程,以函数作为入口,而不是去声明一个对象类,如:
say('one one!')
- RP 响应式编程,一种面向数据流和变化传播的编程范式,如:
a = 5; b=6; c=a+b;
当a
或b
变化的时候,c
会随之变化 - FRP 函数式响应式编程,依赖数据流的函数式编程,如:
str='one one~'; say(str)
, 当str
变化,会自动触发say
基础概念
Nest.js的核心是基于IoC控制反转 + DI 依赖注入 去实现类的声明和实例化的。如果你了解过Spring Boot其实很容易上手nest.js。
Module
Module 其实是nest.js用来将一个web应用拆分成各个子模块的分类规定,web应用根模块一般叫app.module.ts
,官方设计图如下:
Module应该由以下几个部分组成:
- providers: 允许交给模块实例化的类,包括不限于Service等
- controllers:必须实例的controller类
- imports: 模块依赖其他模块
- exports:模块对外提供的方法类
Controller
Controller就一个作用,分割路由,调用处理方法,返回http请求结果。
支持写法:
@Controller('test')
@Get()
@Post()
@Put()
@Del()
代表各种请求方法(http Method)- 还支持一些特殊写法:
@Session()
@Body(key?: string)
@Param(key?: string)
Provider
Provider其实就是不仅仅是Service层,还包括:Sql的Dao层、工具方法等提供。它和其他层关系如下图:
写法:
@Injectable()
声明该类是一个Provider,允许其他类实现依赖注入@Optional()
允许构造不传@Inject()
自动依赖注入
Middleware
Middleware中间件,其实和egg.js的中间件概念一样,就是当http请求来了之后,被中间件处理一遍之后才会到对应的Controller层。
写法:
implements NestMiddleware
,必须实现NestMiddleware
接口,以及内部方法use(req: Request, res: Response, next: NextFunction)
,同时内部方法必须调用next
- 在
Module
层注册中间件,这里需要可以设置
1 | import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common'; |
- 了解
MiddlewareConsumer
,中间件消费者工具类,主要把中间件加上一些配置项功能,如:forRoutes
支持路由匹配,exclude
不包含路由 - 函数式声明中间件,因为Middleware是基于expres,所以写法与express基本上一致
1 | import { Request, Response, NextFunction } from 'express'; |
ExceptionFilter
Filter过滤器,这个应该是所有web框架都具备的功能,拦截用户请求和web返回数据。在Nest.js中,只实现ExeptionFilter,你也可以基于这个去自定义自己的异常过滤器,具体如下图:
写法:
@Catch(HttpException) class HttpExceptionFilter implements ExceptionFilter
实现自定义异常过滤器@UseFilters(new HttpExceptionFilter())
能给具体接口包裹上一层自定义的异常过滤器
Pipe
Pipe 管道流,是指的Http请求里的内容数据流,它支持数据验证、数据转换等功能,有点类似Filter的功能。
写法:
@Param('id', ParseIntPipe) id: number
,将参数id转换为number类型class ValidationPipe implements PipeTransform
自定义 Pipe,同时必须实现方法transform(value: any, metadata: ArgumentMetadata)
@UsePipes(new Pipe())
,支持在controller配置自定义的Pipe
Guard
Guard 守卫,也是处Http请求中的一层特殊中间件,但是与中间件不同的时候,中间件不知道next()是去哪个执行代码,而Guard则可以获取ExecutionContext
实例,可以获知整个请求的生命周期和内置内容,通常用来接口登录和权限控制。
写法:
class AuthGuard implements CanActivate
- 必须实现方法
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean>
- 注册使用:
@UseGuards
Controller层使用,app.useGlobalGuards(new RolesGuard());
全局注册
Interceptor
Interceptor是面向切面编程理念影响的概念,它允许你在方法执行前后扩展原有函数功能,如:改变返回结果,扩展基本功能等,常用的场景:添加常规日志。
写法:
class LoggingInterceptor implements NestInterceptor
,自定义实现intercept(context: ExecutionContext, next: CallHandler): Observable<any>
方法实现,同时需要返回对应结果next()
- 注册使用:
@UseInterceptors(LoggingInterceptor)
可以在类或方法前进行注册,app.useGlobalInterceptors(new LoggingInterceptor());
全局注册
其他
- 自定义参数装饰器
createParamDecorator
,可以从request对象中抽取固定的参数。 applyDecorators
可以将多个装饰器 方法合在一起验证,然后形成一个新的注装饰器
PS: 装饰器是什么?
- 装饰器在JavaScript中暂时是没有,只有TypeScript才可以实现一种语法糖
- 装饰器使用 @expression 的形式,其中 expression 必须能够演算为在运行时调用的函数,其中包括装饰声明信息。
- 自定义装饰器代码如下:
1 | // 这是一个装饰器工厂——有助于将用户参数传给装饰器声明 |
生命周期
Nest.js的生命周期分为三个阶段:初始化、运行和终止,下图详细生命周期的各个子阶段:
允许监听的生命周期函数:
onModuleInit()
, 模块初始化时候调用onApplicationBootstrap()
,所有模块都准备好了,但是在web应用正式启用前会被调用onModuleDestroy()
,模块准备被停止beforeApplicationShutdown()
, web应用准备被停止之前onApplicationShutdown()
,web应用被停止之后,在进程退出之前
上手实战
第一步安装:
1 | npm i -g @nestjs/cli |
生成项目结构
1 | src |
运行
1 | yarn |
打开 http://localhost:3000 就可以访问了。
后面有篇文章叫《从nest.js中了解Ioc和DI》,大家想了解可以去看看。
- 本文作者: qborfy
- 本文链接: https://www.qborfy.com/today/20220110.html
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!