介绍
Nest 是一个用于构建高效,可扩展的 Node.js 服务器端应用程序的框架。它使用渐进式 JavaScript,内置并完全支持 TypeScript(但仍然允许开发人员使用纯 JavaScript 编写代码)并结合了 OOP(面向对象编程),FP(函数式编程)和 FRP(函数式响应编程)的元素。
复制借鉴至NestJs中文文档
PS1: NestJs中文网
安装
与Vue一样,你可以使用脚手架来构建一个Nest项目,或者直接从git上clone源码。这里建议安装一下NestJs的脚手架。。。。因为比较简单
# npm
npm i -g @nestjs/cli
# yarn
yarn global add @nestjs/cli
复制代码
PS2: 依赖Node.js
(ver>=10.13.0)
创建项目
nest new project-name
复制代码
这里选择使用yarn
创建成功,打开文件夹看一下,项目结构如下
src
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
└── main.ts
复制代码
看着这个结构,相信一些做过Java
尤其是用过Spring
框架的同学已经开始激动了,不着急,更令人激动的内容还在后面
PS3: 索尼公司推出的第三代家用电视游戏机
启动
打开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);
// 3000 --> 8081
}
bootstrap();
复制代码
将默认监听端口改成8081,3000空出来给Vue使用
然后运行yarn start:dev
以开发模式启动
访问localhost:8081
,或者也可以使用postman来请求一下
那么,这个Hello World!是怎么返回的呢。
我们打开控制器的代码看一下
// 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();
}
}
复制代码
可以看到,在这里引用并实例化了AppService的类,调用并返回了它的getHello方法。我们再看下service的代码
// app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
复制代码
这样结构就很明了了,至于代码中出现的@开头的这些装饰器,后续会介绍到 可以去看下中文文档,这里就不详细介绍了,相信接触过Spring的同学看着会很眼熟。
为了验证一下猜想,我们把getHello()
的返回值改成'Hello Nest',试一下。
PS4: 如果是以开发模式启动的项目yarn start:dev
,在修改非配置项代码的时候是不需要重启项目的,而如果直接使用yarn start
启动项目,每次修改代码都是需要重新启动的。
新增功能模块
在前面我们知道了,NestJs的项目结构是由Controller、Service、Module
三个主要部分组成的,它们共同组成一个模块。
- Controller: 控制器,通过
@Controller()
装饰器定义的类,目的是接收应用的特定请求。路由机制控制哪个控制器接收哪些请求。通常,每个控制器有多个路由,不同的路由可以执行不同的操作。它的功能类似Spring,主要就是为前端提供api接口,以及一些简单的验证。 - Service: 提供者,又称为Provider,通过
@Injectable()
装饰器定义的类,功能也类似Spring的服务层,主要负责处理具体的业务,逻辑代码一般都写在这里。 - Module: 模块,通过
@Module()
装饰器定义的类,这里和Spring有区别,它的作用主要是负责连接Controller
和Service
,有些类似namespace
。 下面,我们新增一个登录服务的模块来试一下。
首先我们可以输入nest -h
来查看下nest的command列表
Options:
-v, --version Output the current version.
-h, --help Output usage information.
Commands:
new|n [options] [name] Generate Nest application.
build [options] [app] Build Nest application.
start [options] [app] Run Nest application.
info|i Display Nest project details.
update|u [options] Update Nest dependencies.
add [options] <library> Adds support for an external library to your project.
generate|g [options] <schematic> [name] [path] Generate a Nest element.
Available schematics:
┌───────────────┬─────────────┬──────────────────────────────────────────────┐
│ name │ alias │ description │
│ application │ application │ Generate a new application workspace │
│ class │ cl │ Generate a new class │
│ configuration │ config │ Generate a CLI configuration file │
│ controller │ co │ Generate a controller declaration │
│ decorator │ d │ Generate a custom decorator │
│ filter │ f │ Generate a filter declaration │
│ gateway │ ga │ Generate a gateway declaration │
│ guard │ gu │ Generate a guard declaration │
│ interceptor │ in │ Generate an interceptor declaration │
│ interface │ interface │ Generate an interface │
│ middleware │ mi │ Generate a middleware declaration │
│ module │ mo │ Generate a module declaration │
│ pipe │ pi │ Generate a pipe declaration │
│ provider │ pr │ Generate a provider declaration │
│ resolver │ r │ Generate a GraphQL resolver declaration │
│ service │ s │ Generate a service declaration │
│ library │ lib │ Generate a new library within a monorepo │
│ sub-app │ app │ Generate a new application within a monorepo │
│ resource │ res │ Generate a new CRUD resource │
└───────────────┴─────────────┴──────────────────────────────────────────────┘
复制代码
创建模块
Module
使用generate
来创建一个新模块
nest g mo user system
复制代码
这里的g和mo都是使用了别名简写,各指令的别名和描述上面的帮助列表里都有了,上述命令的意义就是在system下生成一个名为login的module。然后我们看下项目结构。
可以看到,对应的路径和文件都已经生成完毕, 当然,只有Module是肯定没用的,我们接下来再创建一下Controller。同样使用脚手架来生成
Controller
# Controller
nest g co user system
复制代码
打开user.module.ts
// user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
@Module({
controllers: [UserController],
})
export class UserModule {}
复制代码
打开app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './system/user/user.module';
@Module({
imports: [UserModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
复制代码
可以看到,新增的Controller已经自动引入进来了,新增的模块也自动被引入了,非常智能(傻瓜?)。
Service
# Service
nest g s user system
复制代码
同样,生成的Service会被自动引入,不需要手动配置
添加接口
我们首先在用户控制器中添加一个注册接口
import { Controller, Post } from '@nestjs/common';
@Controller('user')
export class UserController {
@Post('register')
register() {
return true;
}
}
复制代码
测试一下
- 局部路由前缀
这里引出了路由的概念,由代码可以看出,我们生成的
user
模块下,@Controller
修饰器中自动填入了一个user, 打开@Controller()
的定义
export declare function Controller(prefix: string | string[]): ClassDecorator;
复制代码
可以看到这个入参是前缀的意思,同理,下面的@Post()
就是定义了一个路径为/register的post请求接口,所以我们想要定位到某个控制器下的某个方法,需要访问控制器前缀 + 方法前缀。
- 全局路由前缀
既然是全局,肯定需要在入口文件里设置,在
main.ts
中通过app.setGlobalPrefix()
来设置全局路由
// main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.setGlobalPrefix('api');
await app.listen(8081);
}
复制代码
再测试一下
补充一下逻辑,测试入参
- user.controller.ts
import { Body, Controller, Post } from '@nestjs/common';
import { UserService } from './user.service';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post('register')
// @Body()可以帮助获取request.body中的参数
register(@Body() user: any) {
return this.userService.register(user);
}
}
复制代码
- user.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class UserService {
register(user: any): boolean {
// 这里输出一下,看参数有没有成功接到
console.log(user);
if (user?.userName && user?.pwd && user?.pwd === user?.confirmPwd) {
return true;
} else {
return false;
}
}
}
复制代码
PS5:因为这里是测试逻辑,所以没有加类型约束(anyScript最棒了!),实际开发中一定要完善类型约束。会给我们后续的开发和维护带来很大的帮助
- 测试一下接口
- 看一下控制台输出
到这里,基本的架构就搭建好了。本文章主要用来记录自己学习过程中的一些知识点以及遇到的问题,如果有任何的不足欢迎在评论区指正。谢谢大家了~~ OVO