前端必会的 Node

224 阅读9分钟

本文未全部完成,后续会继续更新,请大家监督!同时在此也祝大家新春快乐!虎年大吉!虎虎生威!

Node 框架对比

Express

Express 是一个保持最小规模的灵活的 Node.js Web 应用程序开发框架,为 Web 和移动应用程序提供一组强大的功能。

github:github.com/expressjs/e…
中文官网:www.expressjs.com.cn/

Koa

koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。
koa 是一个拥有洋葱模型中间件的 http 处理库,一个请求,经过一系列的中间件,最后生成响应。
Koa的大致实现原理:context 上下文的保存和传递,中间件的管理和 next 方法的实现。

中文官网:www.koajs.com.cn/

Egg

阿里团队基于 Koa 来做的。Egg 奉行『约定优于配置』,按照一套统一的约定进行应用开发。
对于开发者来说,Egg 因为是国产,所以文档比较好理解,也比较齐全。但是 EggJS 有2个问题。
一个是依赖注入的问题,导致所有的代码文件是按照功能来归类的,比如所有的控制器代码都会放置在同一个目录下,所有的服务代码也全部放置在 service 目录下,在模块众多的情况下,开发时需要来回切换分散在不同目录下的文件,给开发带来了不便。
另一个是对 TS 的支持天生残缺(egg-ts-helpers),各种第三方库的支持也不受控制。
为了解决这些问题,阿里新出了 Midway 框架。

Midway

Midway 是 Egg 的新一代版本。
midwayjs 一个面向未来的云端一体 Node.js 框架。
Midway 是一个适用于构建 Serverless 服务,传统应用、微服务,小程序后端的 Node.js 框架。
Midway 对于依赖采用了自动扫描的机制,连手动注册依赖的一步都可以省去。
Midway 内部使用了自动扫描的机制,在应用初始化之前,会扫描所有的文件,包含装饰器的文件会自动绑定到容器。
同时使用 TypeScript 开发。但是也有人说 midway 是为了 ts 而 ts。

Nest

Nest.js 背后是国外的 Trilon 团队。Nestjs 是基于 Express 开发的, Koa.js 是 Express.js 原班人马用新理念重新创作的框架,相信是有一定的先进性的,而 Express.js 因为产生历史早,而有更好的生态基础,周边的插件、中间件什么的可能更丰富。
Nest 属于前端 ts 大趋势下深度使用注解特性并提供各种增强开发体验的框架,它提供了一套完整的解决方案,包含了认证、数据库、路由、http 状态码、安全、配置、请求等开箱即用的技术。
内置并完全支持 TypeScript(但仍然允许开发人员使用纯 JavaScript 编写代码)。
Nestjs 采用命令行工具生成相应的文件,比如 provider, module, controller, filter 等等,所以无需手动添加相应的文件。
Nestjs 毕竟是国外的框架,他的生态会比 Midway 的生态更好,这也是我为什么选择 Nestjs 的原因。

中文官网:docs.nestjs.cn/

Nest 最基础的几个概念

控制器(controller)

控制器负责处理传入的请求和向客户端返回响应

要使用 CLI 创建控制器,只需执行 $ nest g controller cats 命令。

介绍 路由,Request, 状态码,Headers, 重定向,路由参数

 Nest 为所有标准的 HTTP 方法提供了相应的装饰器:@Put()@Delete()@Patch()@Options()、以及 @Head()

注册控制器。

/* cats.controller.ts */ 
import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common'; 
import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto';

@Controller('cats')
export class CatsController {
  @Post()
  create(@Body() createCatDto: CreateCatDto) {
    return 'This action adds a new cat';
  }
 
  @Get()
  findAll(@Query() query: ListAllEntities) {
    return `This action returns all cats (limit: ${query.limit} items)`;
  }
 
  @Get(':id')
  findOne(@Param('id') id: string) {
    return `This action returns a #${id} cat`;
  }
 
  @Put(':id')
  update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
    return `This action updates a #${id} cat`;
  }
 
  @Delete(':id')
  remove(@Param('id') id: string) {
    return `This action removes a #${id} cat`;
  }
}

提供者(Providers)

Provider 只是一个用 @Injectable() 装饰器注释的类。可以理解为 Model。

控制器应处理 HTTP 请求并将更复杂的任务委托给 providers

要使用 CLI 创建服务类,只需执行 $ nest g service cats 命令。

注册提供者。

import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';
 
@Module({
  controllers: [CatsController],
  providers: [CatsService],
})
export class AppModule {}

模块(Module)

模块是具有 @Module() 装饰器的类。

nest g module cats

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ProjectController } from './project.controller';
import { ProjectService } from './project.service';
import { ProjectEntities } from '../shared/entity/project.entities';
 
@Module({
  imports: [TypeOrmModule.forFeature([ProjectEntities])],
  controllers: [ProjectController],
  providers: [ProjectService],
})
export class ProjectModule {}

Nest 的第一个 demo

nest new project-name
cd first-nest
npm run start:dev

创建 controller, module, service

nest g controller user
nest g service user
nest g module user

迁移 controller, service

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
 
@Module({
  imports: [UserModule], // 这里
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}
// user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
 
@Module({
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}
// user.service.ts
import { Injectable } from '@nestjs/common';
import { User } from '../mock/user';
import { MockUser } from '../mock/MockUser';
 
@Injectable()
export class UserService {
  private mockUser = new MockUser();
 
  public findAll(): Array<User> {
    return this.mockUser.data;
  }
 
  public add(user: User): User {
    this.mockUser.data.push(user);
    return user;
  }
 
  public update(user: User): User {
    this.remove(user.id);
    this.mockUser.data.push(user);
    return user;
  }
 
  public remove(id: number): void {
    this.mockUser.data = this.mockUser.data.filter((v) => v.id != id);
  }
 
  public findOne(id: number): User {
    return this.mockUser.data.find((v) => v.id == id);
  }
}
// user.controller.ts
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  HttpCode,
  Param,
  Delete,
} from '@nestjs/common';
import { User } from '../mock/user';
import { UserService } from './user.service';
import { InputUser } from '../mock/input';
import { UpdateUser } from '../mock/update';
 
@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}
 
  /**
   * findAll
   */
  @Get()
  @HttpCode(200)
  public findAll(): Array<User> {
    return this.userService.findAll();
  }
 
  @Post()
  @HttpCode(201)
  public add(@Body() input: InputUser): User {
    if (input.id === undefined) {
      throw new Error('未带入ID');
    }
    const user: User = {
      ...input,
    };
    return this.userService.add(user);
  }
 
  @Patch(':id')
  @HttpCode(203)
  public update(@Param() param, @Body() update: UpdateUser): User {
    const userInfo = this.userService.findOne(param.id);
    if (!userInfo) {
      throw new Error('该用户不存在');
    }
    userInfo.no = update.no;
    userInfo.name = update.name;
    return this.userService.update(userInfo);
  }
 
  @Delete(':id')
  @HttpCode(204)
  public remove(@Param() param): void {
    this.userService.remove(param.id);
  }
 
  @Get(':id')
  @HttpCode(200)
  public findOne(@Param() param): User {
    return this.userService.findOne(param.id);
  }
}

接口验证

1、获取所有的用户信息  http://localhost:3000/user

2、新增用户信息 post http://localhost:3000/user?id=1&no=test1&name=zhangsan

3、修改用户信息 patch http://localhost:3000/user/1 

no: test2

name: lisi

4、根据id 获取用户信息get http://localhost:3000/user/1

5、删除某条用户信息 delete http://localhost:3000/user/1

参数区别

Query: Query Params中获取

Body: body中获取

Param: url中获取

Nest 数据库连接

创建数据库表

1、SQL语句创建

验证数据库的安装

mysqladmin --version
// 显示的结果
mysqladmin  Ver 8.0.25 for macos11.3 on x86_64 (Homebrew)

链接数据库

// mysql -h 主机名 -u 用户名 -p
mysql -h 127.0.0.1 -u root -p
// 输入密码
Enter password:  123456

管理数据库的命令

// SHOW DATABASES: 列出 MySQL 数据库管理系统的数据库列表。
SHOW DATABASES;
// USE 数据库名 : 选择要操作的Mysql数据库,使用该命令后所有Mysql命令都只针对该数据库。
USE demo;
// SHOW TABLES: 显示指定数据库的所有表,使用该命令前需要使用 use 命令来选择要操作的数据库。
SHOW tables;
// SHOW COLUMNS FROM 数据表:  显示数据表的属性,属性类型,主键信息 ,是否为 NULL,默认值等其他信息。
SHOW COLUMNS FROM users;
// SHOW INDEX FROM 数据表: 显示数据表的详细索引信息,包括PRIMARY KEY(主键)
SHOW INDEX FROM users;

创建数据库,删除数据库

// CREATE DATABASE 数据库名;
CREATE DATABASE name_database;
// DROP DATABASE <数据库名>;
DROP DATABASE name_database;

创建数据库表,删除数据库表

// CREATE TABLE table_name (column_name column_type): 创建数据库表
CREATE TABLE IF NOT EXISTS `runoob_tbl`(
  `id` INT UNSIGNED AUTO_INCREMENT,
  `no` VARCHAR(225) NOT NULL,
  `number` VARCHAR(225) NOT NULL,
  `submission_date` DATE,
  PRIMARY KEY ( `id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

// DROP TABLE table_name :删除数据库表:

2、navicat操作

新建数据库,新建表

TypeORM 集成

TypeORM 框架是对象关系映射框架。通常,对象部分是指应用程序中的域/模型,关系部分是关系数据库管理系统中的表之间的关系(例如Oracle, MYSQL等),最后,映射部分是指桥接模型和表格的行为。

ORM 是一种将实体与数据库表进行映射的工具。

nest js TypeORM 文档:docs.nestjs.cn/8/technique…

TypeORM 中文文档:typeorm.biunav.com/zh/

TypeORM 英文文档:typeorm.io/#/connectio…

1、安装依赖

npm install --save @nestjs/typeorm typeorm mysql2

2、将 TypeOrmModule 导入 AppModule

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './user/user.module';
import { UsersqlModule } from './usersql/usersql.module';
// TypeOrmModule导入
import { TypeOrmModule } from '@nestjs/typeorm';
import { Connection } from 'typeorm';
import { UserEntity } from './shared/entity/user.entity';
 
@Module({
  imports: [
    // TypeOrmModule 导入
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: '127.0.0.1',
      port: 3306,
      username: 'root',
      password: '123456',
      database: 'nest_user',
      entities: [], // 添加entity
      synchronize: true, // 这里这个真实环境不能是true
    }),
    UserModule,
    UsersqlModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {
  // 链接上
  constructor(private readonly connection: Connection) {}
}

3、修改entity实体

1)新增entity实体

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
 
@Entity({ name: 'user' })
export class UserEntity {
  @PrimaryGeneratedColumn()
  id: number;
 
  @Column()
  no: string;
 
  @Column()
  name: string;
}

2)导入userEntity到app.module中

entities: [UserEntity]

3)修改usersql.module.ts

import { Module } from '@nestjs/common';
import { UsersqlController } from './usersql.controller';
import { UsersqlService } from './usersql.service';
import { UserEntity } from 'src/shared/entity/user.entity';
// 新增
import { TypeOrmModule } from '@nestjs/typeorm';
 
@Module({
  // 新增
  imports: [TypeOrmModule.forFeature([UserEntity])],
  controllers: [UsersqlController],
  providers: [UsersqlService],
})
@Module({})
export class UsersqlModule {}

4)修改usersql.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { UserEntity } from 'src/shared/entity/user.entity';
import { Repository } from 'typeorm';
import { User } from '../mock/user';
 
@Injectable()
export class UsersqlService {
  constructor(
    @InjectRepository(UserEntity)
    private userRepository: Repository<UserEntity>,
  ) {}
 
  async findAll(): Promise<UserEntity[]> {
    return await this.userRepository.find();
  }
 
  async add(user: UserEntity): Promise<any> {
    await this.userRepository.save(user);
  }
 
  async update(user: User): Promise<UserEntity> {
    const currentUser = await this.userRepository.findOne(user.id);
    currentUser.no = user.no;
    currentUser.name = user.name;
    await this.userRepository.save(currentUser);
    return currentUser;
  }
 
  async findOne(id: number): Promise<UserEntity> {
    return await this.userRepository.findOne(id);
  }
 
  async remove(id: number): Promise<void> {
    await this.userRepository.delete(id);
  }
}

5) 修改usersql.controller.ts

import {
  Body,
  Controller,
  Delete,
  Get,
  HttpCode,
  Param,
  Patch,
  Post,
} from '@nestjs/common';
import { InputUser } from 'src/mock/input';
import { UpdateUser } from 'src/mock/update';
import { UsersqlService } from 'src/usersql/usersql.service';
import { User } from '../mock/user';
 
@Controller('usersql')
export class UsersqlController {
  constructor(private readonly UsersqlService: UsersqlService) {}
 
  @Get()
  @HttpCode(200)
  async findAll(): Promise<User[]> {
    return await this.UsersqlService.findAll();
  }
 
  @Post()
  @HttpCode(200)
  async add(@Body() input: InputUser) {
    if (input.id === undefined) {
      throw new Error('未带入ID');
    }
    const user: User = {
      ...input,
    };
    return await this.UsersqlService.add(user);
  }
 
  @Patch(':id')
  @HttpCode(203)
  async update(@Param() param, @Body() update: UpdateUser) {
    const userInfo = await this.UsersqlService.findOne(param.id);
    if (!userInfo) {
      throw new Error('该用户不存在');
    }
    userInfo.no = update.no;
    userInfo.name = update.name;
    return await this.UsersqlService.update(userInfo);
  }
 
  @Delete(':id')
  @HttpCode(204)
  async remove(@Param() param) {
    await this.UsersqlService.remove(param.id);
  }
 
  @Get(':id')
  @HttpCode(200)
  async findOne(@Param() param) {
    return await this.UsersqlService.findOne(param.id);
  }
}

备注:其他entity

import {
  Entity,
  Column,
  PrimaryGeneratedColumn,
  CreateDateColumn,
  UpdateDateColumn,
} from 'typeorm';
 
@Entity({ name: 'platform_user_version' })
export class UserVersionEntities {
  // 主键装饰器
  @PrimaryGeneratedColumn({ comment: '用户自增ID' })
  id: number;
 
  @Column({ name: 'project_id', comment: '项目id' })
  project_id: number;
 
  @Column({ name: 'version', comment: '版本号' })
  version: string;
 
  // @Column({ name: 'create_at', type: 'timestamp', comment: '创建时间' })
  // create_at: string;
 
  // @Column({ name: 'update_at', type: 'timestamp', comment: '更新时间' })
  // update_at: string;
 
  @CreateDateColumn({ name: 'create_at', comment: '创建时间' }) // 自动生成列
  create_at: string;
 
  @UpdateDateColumn({ name: 'update_at', comment: '更新时间' }) // 自动生成并自动更新列
  update_at: string;
 
  @Column({ name: 'uid', default: 0, comment: '用户id' })
  uid: string;
}

Query builder

QueryBuilder是 TypeORM 最强大的功能之一 ,它允许你使用优雅便捷的语法构建 SQL 查询,执行并获得自动转换的实体。

中文文档:typeorm.biunav.com/zh/select-q…

使用 repository:

import { getRepository } from "typeorm";
 
const user = await getRepository(User)
  .createQueryBuilder("user")
  .where("user.id = :id", { id: 1 })
  .getOne();

有 5 种不同的QueryBuilder类型可用:

  1. SelectQueryBuilder- 用于构建和执行SELECT查询。 例如:
import { getConnection } from "typeorm";
 
const user = await getConnection()
  .createQueryBuilder()
  .select("user")
  .from(User, "user")
  .where("user.id = :id", { id: 1 })
  .getOne();
  1. InsertQueryBuilder- 用于构建和执行INSERT查询。 例如:
import { getConnection } from "typeorm";
 
await getConnection()
  .createQueryBuilder()
  .insert()
  .into(User)
  .values([{ firstName: "Timber", lastName: "Saw" }, { firstName: "Phantom", lastName: "Lancer" }])
  .execute();
  1. UpdateQueryBuilder- 用于构建和执行UPDATE查询。 例如:
import { getConnection } from "typeorm";
 
await getConnection()
  .createQueryBuilder()
  .update(User)
  .set({ firstName: "Timber", lastName: "Saw" })
  .where("id = :id", { id: 1 })
  .execute();
  1. DeleteQueryBuilder- 用于构建和执行DELETE查询。 例如:
import { getConnection } from "typeorm";
 
await getConnection()
  .createQueryBuilder()
  .delete()
  .from(User)
  .where("id = :id", { id: 1 })
  .execute();
  1. RelationQueryBuilder- 用于构建和执行特定于关系的操作[TBD]。

使用QueryBuilder获取值

const timber = await getRepository(User)
  .createQueryBuilder("user")
  .where("user.id = :id OR user.name = :name", { id: 1, name: "Timber" })
  .getOne();
const users = await getRepository(User)
  .createQueryBuilder("user")
  .getMany();