初识Nest心路历程
作为一名前端开发,说实话,学习Nest后端技术, 会有一定的成本。我试着阅读文档,安装项目,把项目跑起来,
当我看到久违的Hellow world 后,还来不及欣喜,就困惑了, 作为一个后端框架,数据不是都会在浏览器 NetWork里看返回的数据,这怎么还直接显示在页面了
?
这让我对 MVC的理解,又有点模糊了 。
当我正要查看代码,发现和vue项目的整体结构还差不多,但是当我打开具体的目录,又懵逼了,@Controller(), @Get(), @Module
这都什么玩意,当我再起看文档
依赖注入,控制反转,装饰器,中间件,管道
看到这些名词,就已经把我劝退了。
当我再看到 模块,中间件,异常过滤器,守卫,拦截器
又感觉还能学,这不就是模块,axios请求,拦截,路由守卫那一部分的知识。
终于当我历经几天的犹豫,决定入坑Nest后端框架,下面就让我们开始学习Nest系列文章
项目脚手架 安装
使用脚手架Nest CLI 构建项目
// 安装全局@nestjs/cli
1. npm i -g @nestjs/cli
// 创建一个nest项目
2. nest new project-name
// 运行项目
3. pnpm run start:dev
选择一个包管理工具,静待安装完毕
运行项目
项目目录说明
+-- dist[目录] // 编译后的目录,用于预览项目
+-- node_modules[目录] // 项目使用的包目录,开发使用和上线使用的都在里边
+-- src[目录] // 源文件/代码,程序员主要编写的目录
| +-- app.controller.spec.ts // 对于基本控制器的单元测试样例
| +-- app.controller.ts // 控制器文件,可以简单理解为路由文件
| +-- app.module.ts // 模块文件,在NestJS世界里主要操作的就是模块
| +-- app.service.ts // 服务文件,提供的服务文件,业务逻辑编写在这里
| +-- app.main.ts // 项目的入口文件,里边包括项目的主模块和监听端口号
+-- test[目录] // 测试文件目录,对项目测试时使用的目录,比如单元测试...
| +-- app.e2e-spec.ts // e2e测试,端对端测试文件,测试流程和功能使用
| +-- jest-e2e.json // jest测试文件,jset是一款简介的JavaScript测试框架
+-- .eslintrc.js // ESlint的配置文件
+-- .gitignore // git的配置文件,用于控制哪些文件不受Git管理
+-- .prettierrc // prettier配置文件,用于美化/格式化代码的
+-- nest-cli.json // 整个项目的配置文件,这个需要根据项目进行不同的配置
+-- package-lock.json // 防止由于包不同,导致项目无法启动的配置文件,固定包版本
+-- package.json // 项目依赖包管理文件和Script文件,比如如何启动项目的命令
+-- README.md // 对项目的描述文件,markdown语法
+-- tsconfig.build.json // TypeScript语法构建时的配置文件
+-- tsconfig.json // TypeScript的配置文件,控制TypeScript编译器的一些行为
src目录下的文件说明
src目录是日常工作编写代码的主要目录,从基本的目录结构也可以对NestJS编写模式有很好的了解。
+-- src[目录] // 源文件/代码,程序员主要编写的目录
| +-- app.controller.spec.ts // 对于基本控制器的单元测试样例
| +-- app.controller.ts // 控制器文件,可以简单理解为路由文件
| +-- app.module.ts // 模块文件,在NestJS世界里主要操作的就是模块
| +-- app.service.ts // 服务文件,提供的服务文件,业务逻辑编写在这里
| +-- app.main.ts // 项目的入口文件,里边包括项目的主模块和监听端口号
细节的东西不用关心太多,我们需要先把整个项目的架构,大体运行机制搞明白,才能进行下面细致入微的学习
1. 从app.main.ts 入口文件开始看起
-
通过 @nestjs/core 导入
NestFactory
, -
主要作用是Use NestFactory to create an application instance
使用NestFactory创建应用程序实例
-
然后使用实例设置监听端口
这就跟我们在 vue 中一样,创建一个实例
import { createApp } from 'vue' import App from './App.vue' const app = createApp(App)
我们是把html 文件中的
<div id="app"></div>
挂载到 实例上 app.mount(‘#app’)而在Nest 中,是把AppModule(模块),挂载到实例上。
2. 那具体AppModule 是什么呢?
AppModule这个类 啥也没干,就导出了? export class AppModule {}
其实不然,这里遇到了第一个新的概念: 装饰器
@Module
- 当类被 module 装饰器装饰的时候,它就是一个模块
。从代码层面来看,这个模块的作用就是组合AppController 和 AppService ,把他们聚合在一起,供 AppModule 使用
- AppController 是什么呢?
这个实例并不明显,因为没有体现出他的路由作用,那要是我这样写,你或许就明白了
@Get('index') 就是请求方法类型, 参数是路径/参数
在里面调用了appService.getHello
的方法 来返回具体的结果
从前端的角度来看Nest的执行顺序
我们请求一个接口 大致流程 是这样的 传递路径/参数
服务端返回对应的结果
axios.get('/user?ID=12345')
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
服务端,在 controller 层会对路由做一个匹配 匹配到响应的路由
,service 去做业务逻辑/和数据库交互
疑问点答疑
1. 全篇都是export class ,怎么没见new 实例化调用呢?
在 NestJS 中,大部分组件(如控制器、服务、模块等)都是通过 class 来定义的,而且通常不需要显式地使用 new
关键字来实例化这些组件。这是因为 NestJS 基于依赖注入(Dependency Injection)的设计理念,在运行时由 NestJS 框架负责创建和管理组件的实例。
这也是我在学Nest最大的一个疑惑,也是Nest中比较伟大的一个设计,@Injectable,@Controller,下面跟了一个class ,就代表这个class 是可以被注入的,换句话说,就是这个class ,我Nest 会自动帮你进行 new 操作,你直接调用里面的方法举行
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello(); // AppService 这个类,没有new,就可以直接使用实例上的方法,是因为她是可以被注入的,也就是有@Inject,Nest 会自动帮我们注入到AppController中
}
}
依赖注入(Dependency Injection)
依赖注入是一种设计模式,通过它,类的依赖关系可以在运行时动态地注入到类中
,而不是在类内部直接创建依赖的实例
。在 NestJS 中,组件之间的依赖关系通过依赖注入实现,这样可以更好地解耦组件之间的关系,提高代码的可测试性和可维护性。
举个例子,当你在一个 NestJS 控制器的构造函数中声明一个服务作为参数时,NestJS 框架会自动注入该服务的实例,而不需要手动创建。例如:
typescriptCopy Code
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller('users')
export class UserController {
// 注入的方式可以是构造器注入:
constructor(private readonly appService: AppService) {}
// 或者属性注入:
// @Inject(AppService)
// private readonly appService: AppService
@Get()
findAll(): string {
return this.appService.findAll();
}
}
在上面的例子中,AppService
被声明为 UserController
的构造函数的参数,通过 TypeScript 的语法,private readonly appService: AppService
相当于声明了一个私有成员变量,并将传入的 AppService
实例赋给了这个成员变量。NestJS 框架在初始化 UserController
时,会自动创建并注入 AppService
的实例。
这种方式使得代码更加简洁,同时也减少了对全局状态的依赖,使得组件更容易被复用和测试。
因此,在 NestJS 中,通常不需要手动使用 new
关键字来实例化,框架会在需要的时候自动处理组件的创建和注入。
这个和前端模块很相似,模块有导出,就有导入,才能使用。而 在Nest中,模块导出,需要导入并且使用new 去实例化类,但是不是通过new 来实例化,而是通过依赖注入的方式去实例化,并且后续调用实例上的方法
总结
- 可以使用cli快速搭建项目,并且运行项目
- 对每个目录进行分析,掌握整体项目结构
- 从app.main.ts 出来,开始 逐步分析,通过NestFactory创建一个实例,然后把模块传入
- 模块就是一个集合,里面聚合了Controller,Service
- Controller 就是一个控制器文件,可以简单理解为路由文件,里面会设置请求方法/请求参数, 然后调用Service 类 来做业务逻辑处理/和数据库交互
- 这么多的class ,都不需要new 去实例化调用,而且调用关系/调用顺序怎么处理,最主要的是利用了依赖注入这个设计模式,类的依赖关系可以在运行时动态地注入到类中,而不是在类内部直接创建依赖的实例。
- 依赖注入可以是构造器注入,也可以是属性注入,相比构造器注入更简便,但是属性注入易读性更强
- 只要是类,只要被装饰器修饰,类的依赖关系可以在运行时动态地注入到类中,而不是在类内部直接创建依赖的实例
后记
后面会陆续完成装饰器/CURD/ 中间件/日志/api 文档/Nest项目实战 等相关文章,敬请期待~~~