从0~1手写Nest.js - (1)搭建Nest服务器,手写装饰器 初步实现@nest/core @nest/common

366 阅读4分钟

一.前言

既然说要从0~1手搓Nest,那就必然要从最最最基础的工程来写起

大家如果使用过Nest官方提供的CLI成功创建过工程的话,会知道Nest的基础工程目录结构是如下这样子的.

image.png

分析一下这几个文件的作用:

  • app.module.ts 根模块
  • app.controller.ts 控制器
  • app.service.ts 服务处理层
  • app.controller.spec.ts 测试文件
  • main.ts 主文件,用于创建Nest实例并启动Nest应用

理解了初始化文件的作用,下面就对比着源代码来一比一的来搭建自己的Nest应用.

哎先等等,下面就要开始写代码了哦,所以目前您最好先创建一个空工程,并初始化package.json,配置好ts.config,让我们来开始手写Nestjs之旅吧~

示例空目录:

image.png

tsconfig.json配置

{
	"compilerOptions": {
		"experimentalDecorators": true, // 启用装饰器功能
		"target": "ES2021",
		"module": "CommonJS",
		"declaration": true,
		"removeComments": true, 
		"emitDecoratorMetadata": true, // 为运行时提供额外的类型信息(可选)
		"allowSyntheticDefaultImports": true,
		"sourceMap": true,
		"outDir": "./dist",
		"baseUrl": "./",
		"incremental": true,
		"skipLibCheck": true,
		"strictBindCallApply": false,
		"forceConsistentCasingInFileNames": false,
		"noFallthroughCasesInSwitch": false,
		"esModuleInterop": true,
	}
}

二.分析源码并搭建自己的Nest应用

1. 分析main.ts

  • 1.在@nestjs/core核心包中导入NestFactory模块,它用于创建nest应用的实例
  • 2.导入App根模块
  • 3.定义了一个异步函数(bootstrap),用来创建nest实例并启动应用
  • 4.使用NestFactory的静态方法create创建一个nest应用实例,并传入AppModule根模块
  • 5.让nest应用实例监听3000端口启动HTTP服务器
  • 6.执行bootstrap函数启动Nest应用
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服务器起到主要作用的是@nestjs/core包下的NestFactory模块和他暴露出的create/listen这两个静态方法,那么好,现在我们就来实现一个自己的NestFactory模块

写代码之前,必须要清楚的是,Nest.js是依赖express 借鉴angular spring所诞生的nodejs服务端框架,所以express是很重要的,因为Nestjs源码中的NestFactory模块就是依赖的express所编写的.

2.实现自己的NestFactory模块

  • 在src目录下新建一个我们自己的@nestjs目录,
  • 在@nestjs目录下新建core目录
  • 在core目录下新建nestFactory.ts
  • 在core目录下新建NestApplication.ts
  • 在core目录下新建index.ts

NestApplication(创建Nest应用实例与HTTP服务端口)

NestApplication.ts

// 因为需要使用express构建服务所以需要安装两个依赖: pnpm i express @types/exporess

// 引入express
import express from "express"
// 引入express类型
import type { Express } from "@types/express"

export class NestApplication {
    // 将express实例私有化
    private readonly app: Express = express()
    
    // protected readonly module等同于 this.module = module
    construtor(protected readonly module) {}
    
    // 启动Http服务器
    async listen(port: number) {
        this.app.listen(port)
    }
}

nestFactory.ts

// 引入NestApplication模块
import { NestApplication } from "./NestApplication"

// 创建NestFactory类
export class NestFactory {
    // 声明静态方法create,用于创建Nest实例,接收一个模块
    static create(module: any) {
        // 创建Nest应用实例,这里抽离出另一个类NestApplication,方便维护与扩展
        const app = new NestApplication(module)
        
        // 返回Nest应用实例
        return app
    }
}

3.index.ts

export * from "./nestFactory"
export * from "./nestApplication"

4.实现自己的main.ts

// 引入自己的@nestjs/core中的NestFactory模块
import { NestFactory } from "./@nestjs/core"
// 引入App根模块
import { AppModule } from "./app.module"

// 定义了一个异步函数(bootstrap),用来创建nest实例并启动应用
async const bootstrap = () => {

    // 使用NestFactory的静态方法create创建一个nest应用实例,并传入AppModule根模块
    const app = await NestFactory.create(AppModule)
    
    // 让nest应用实例监听3000端口启动HTTP服务器
    await app.listen(3000)
}

// 执行bootstrap函数启动Nest应用
bootstrap()

--- end NestFactory核心模块完成

三.实现自己的app.controller.ts与手写控制器装饰器

  • 在@nestjs目录下新建common目录,用于存放各种装饰器
  • 在common目录下新建controller.decorator.ts (控制器装饰器)
  • 在common目录下新建index.ts

1. index.ts

export * from "./controller.decorator"

2.实现自己的控制器装饰器 controller.decorator.ts(待完善)

// 需要依赖元数据,所以要安装reflect-metadata: pnpm i reflect-metadata

// 导入元数据包
import "reflect-metadata"

export const Controller = () :ClassDecorator => {
	return (target: Function) => {
		// todo
	}
}	 

app.controller.ts

import { Controller } from "./@nestjs/common/controller.decorator";

@Controller()
export class AppController {}

四.实现自己的app.module.ts与手写模块装饰器

  • 在common目录下新建module.decorator.ts (模块装饰器)

1. index.ts

export * from "./module.decorator"

2.实现自己的模块装饰器 module.decorator.ts(待完善)

// 需要依赖元数据,所以要安装reflect-metadata: pnpm i reflect-metadata

// 导入元数据包
import "reflect-metadata"

// 定义参数类型
interface ModuleMetadata {
	controllers: Function[]
}

/**
 * 
 * @param metadata 类的数组
 * @returns 类装饰器(模块装饰器)
 */
export const Module = (metadata: ModuleMetadata) :ClassDecorator => {
	return (target: Function) => {
		// todo
	}
}	 

app.module.ts

import { Module } from "./@nestjs/common/module.decorator"
import { AppController } from "./app.controller"

@Module({
	controllers: [AppController]
})
export class AppModule {}

五.运行Nest应用

  • package.json添加运行指令, 需要依赖ts-node运行项目
// pnpm i ts-node
  "scripts": {
    "start": "ts-node src/main.ts", 
  },
  • 访问localhost:3000,如果出现GET NOTFOUND则代表启动成功!
  • 完结,下篇文章再见~