掘金 后端 ( ) • 2024-04-24 10:40

新建项目

新建Nest项目可以通过以下两条命令:

 npm i -g @nestjs/cli
 nest new project-name

首先,我们在终端执行npm i -g @nestjs/cli,全局安装Nest的脚手架nestjs/cli

接着,我们随便选择一个位置,打开终端后继续执行nest new project-name,这里的project-name可以输入你建的nest项目名称,这里我的项目名称是demo1。之后选择完包管理器,回车!等待依赖安装完毕后,我们的第一个Nest项目就装好了。

image-20240423165652932.png

通过命令cd demo1进入项目后,执行npm run start,即可成功启动Nest服务:

image-20240423165927023.png

然后打开浏览器,访问localhost:3000,即可看到Nest服务的响应结果:

image-20240423170933211.png

到这一步后,恭喜你,你已经成功创建了一个后端的服务。至此,后端的大门正式为你打开!

目录结构和流程梳理

创建的项目会具有以下的目录:

 demo1
 ┣━━ src
 ┃    ┣━━ app.controller.spec.ts
 ┃    ┣━━ app.controller.ts
 ┃    ┣━━ app.module.ts
 ┃    ┣━━ app.service.ts
 ┃    ┗━━ main.ts
 ┣━━ .eslintrc.js
 ┣━━ .gitignore
 ┣━━ .prettierrc
 ┣━━ .nest-cli.json
 ┣━━ package.json
 ┣━━ pnpm-lock.yaml
 ┣━━ README.md
 ┣━━ tsconfig.build.json
 ┗━━ tsconfig.json

我们着重了解Nest项目独有的,也就是根目录下的.nest-cli.json文件和src里的文件。

配置文件.nest-cli.json

.nest-cli.json里有如下内容:

 {
   "$schema": "https://json.schemastore.org/nest-cli",
   "collection": "@nestjs/schematics",
   "sourceRoot": "src",
   "compilerOptions": {
     "deleteOutDir": true
   }
 }

这是一个json配置的对象,首先$schema字段是个JSON Schema文件的地址,可以用来验证本JSON 文件是否符合预期的格式和规范。我们可以复制它的值到浏览器里,里面会有每个字段的详细解释。

其次collection字段指定了schematic 集合,这种集合是一种集合了多种自动化任务的schema形式工具集。这里使用了 @nestjs/schematics,表示使用Nest.jsschematics 集合来生成代码和配置项目。我们可以通过执行npm show @nestjs/schematics --json查看具体信息。

接着是sourceRoot,它指定了项目源代码的根目录,默认是src

最后是compilerOptions,它是一个编译选项集合的对象,这里声明的deleteOutDir属性的值为true,表示每次删掉之前的编译目录。而编译目录的位置就在根目录下的tsconfig.json文件的outDir字段里指定。

src目录

src目录下有五个文件,我们详细说下前面四个文件的含义和作用。

文件名 含义 main.ts 应用程序的入口文件,它使用核心函数 NestFactory 创建一个Nest应用程序实例。 app.service.ts 声明了一个模块的服务。 app.module.ts 这是一个模块,且是应用程序的根模块。作用是将servicecontroller链接起来。 app.controller.ts 声明了路由的控制器。 app.controller.spec.ts 控制器的单元测试。

main.ts

 import { NestFactory } from '@nestjs/core';
 import { AppModule } from './app.module';
 ​
 async function bootstrap() {
   const app = await NestFactory.create(AppModule);
   await app.listen(3000);
 }
 bootstrap();

首先是main.ts,这个文件是Nest服务的入口文件。上述代码首先引入了Nest的构造器NestFactory,该构造器对象有一个create方法,此方法能接收一个主模块,生成一个Nest实例,然后在第六行,我们让Nest实例监听3000端口。

image-20240424092350075.png

以上便是Nest模块的入口代码,非常简单。

app.module.ts

 import { Module } from '@nestjs/common';
 import { AppController } from './app.controller';
 import { AppService } from './app.service';
 ​
 @Module({
   imports: [],
   controllers: [AppController],
   providers: [AppService],
 })
 export class AppModule {}

上述代码首先引入Module函数,该函数是一个装饰器,其入参是一个包含输入模块数组imports字段、路由控制器数组controllers字段、以及提供了服务处理的providers字段的对象。被Module装饰器装饰后的AppModule类,便成为了一个合法的Nest模块。

image-20240424092728848.png

那么Module装饰器里接收的AppControllerApService具体又是干什么的呢?我们继续往下看代码。

app.controller.ts

 import { Controller, Get } from '@nestjs/common';
 import { AppService } from './app.service';
 ​
 @Controller()
 export class AppController {
   constructor(private readonly appService: AppService) {}
 ​
   @Get()
   getHello(): string {
     return this.appService.getHello();
   }
 }

app.controller.ts里,类AppController经过装饰器Controller函数装饰过后,将变成一个合法的Nest路由监听器,这里监听的路由在第8行@Get()装饰器声明,Get装饰器的入参为空,代表监听的是最简单的路由/,监听的请求类型是get类型。同理,Post装饰器监听的就是post类型,这里我们后面再说。

我们浏览器访问localhost:3000的时候,其实是在访问localhost:3000/,这时/路由就被@Get()装饰器捕捉。路由被捕捉后的处理方法是被@Get()装饰的getHello方法,该方法返回了AppService类的实例对象的getHello方法的处理结果。

这里有两个getHello方法,我们需要注意区分:一个是路由监听器的处理方法,另一个是AppService服务里定义的getHello方法。

image-20240424091315031.png

不知道大家是否注意到,我们在第10行用到了实例appService对象,但是这个对象却只是在第6行构造函数的入参里声明过,怎么就能在类的其他地方使用了呢?构造函数入参里声明的变量不是只能在构造函数内部使用吗?这个问题我们留在后面IoCDI一节讲。

app.service.ts

接下来我们继续讲最后的app.service.ts文件。

 import { Injectable } from '@nestjs/common';
 ​
 @Injectable()
 export class AppService {
   getHello(): string {
     return 'Hello World!';
   }
 }

上述代码更加简单,只是返回了一个AppService类,该类提供了一个getHello的方法,并在该方法内部返回了一个Hello World!的字符串。唯一一个与普通类不同的地方在于,这个类是被装饰器函数Injectable装饰过的。

image-20240423193909162.png

最后还剩一个app.controller.spec.ts文件,这个文件有点特殊,它是以.spec.ts结尾。这种结尾的文件在Nest中是测试文件,它不用参与主流流程的构建,为了降低学习的心智负担,因此我们这里就先不介绍,感兴趣的同学可以自行了解。

src下的主要文件main.tsapp.module.tsapp.controller.tsapp.module.ts我们就介绍完了,接下来我们再梳理一下整体的流程:

  1. Nestmain.ts中通过NestFactory.create方法创建了一个Nest实例,然后让该实例监听3000端口。这里是整个Nest服务的入口。
  2. create需要一个模块作为入参,故Nestapp.module.ts中声明了AppModule,并传给了create方法。
  3. AppModule类通过装饰器Module关联了AppControllerAppService
  4. AppService类也有一个装饰器Injectable,同时声明了一个getHello方法,并返回了我们熟悉的Hello World!字符串。
  5. AppController稍微复杂一点,被Controller装饰器装饰后的它成为了一个路由监听器。同时在类内部,通过@Get装饰器监听了一个/路由的get请求。我们在浏览器里访问localhost:3000就会被@Get()装饰器捕获。捕获后执行了getHello方法,该方法内部返回了一个AppService类实例的getHello方法的执行结果,最后我们就看到了Hello World!

image-20240424092943424.png

总结

这节介绍了如何安装NestJS脚手架,并通过脚手架快速搭建一个基础的Nest服务,接着我们介绍了Nest项目的目录结构,并详细讲解了Nest服务的基础流程。

同时我们遗留了一个问题作为大家的思考题:

  • 在文件app.controller.ts里,第10行用到了实例appService对象,但是这个对象却只是在第6行构造函数的入参里声明过,怎么就能在类的其他地方使用了呢?

以上即本节全部内容,如果你喜欢本节内容,来个点赞👍🏻吧~