
前言
NodeJs在前端領(lǐng)域的作用不言而喻,做為前端的“基石”,在打包工具、腳手架、前端工程中間層(模板渲染、接口轉(zhuǎn)發(fā))、Server端中應(yīng)用廣泛,掌握NodeJs開發(fā)技能已經(jīng)是每個前端開發(fā)者的必備技能之一,今天這篇文章,就介紹一下,內(nèi)部自研項目使用NodeJs全棧開發(fā)的落地經(jīng)歷。
背景
在低代碼平臺的浪潮中,我司也自研了自己的低代碼平臺,其中,前端采用可視化拖拽引擎 + 解析器的模式,以jsonSchema為媒介,來實現(xiàn)低代碼開發(fā)模式,這里就需要一個穩(wěn)定的Server端來支持其持久化數(shù)據(jù)存儲以及其平臺業(yè)務(wù)流轉(zhuǎn)。我們采用了NestJs + MongoDB的技術(shù)方案,下面是一個簡單的架構(gòu)圖,一些技術(shù)細節(jié)不便公開,所以web層的技術(shù)架構(gòu)簡寫,本文我們只介紹Server端

其中
- 數(shù)據(jù)庫使用
MongoDB - Server端核心框架使用
NestJs - oa登錄使用內(nèi)部
express中間件 - 前端部署使用node模板
- 對接第三方(如:權(quán)限系統(tǒng))使用
scf
技術(shù)選型
?? 為什么是NestJs?
首先,nestjs是基于express的一款企業(yè)級應(yīng)用框架(默認express,還可支持Fastify,本文只談express),nodejs的完整框架有很多,如express、koa2、eggjs等。但我們選擇了nestjs,其主要原因是內(nèi)部生態(tài);我們知道,express和koa2中間件的基本語法不同,express是基于promise的,而koa2是基于async/await的,所以,express中間件和koa2中間件是不通用的。而團隊內(nèi)部的沉淀的中間件(如:登錄中間件)都是express生態(tài),這是我們選擇nestjs最主要原因;另外,nestjs的周邊生態(tài)豐富、并完全支持typescript,于是我們堅定的使用了nestjs。
??為什么是MongoDB?
行業(yè)內(nèi)有很多優(yōu)秀的數(shù)據(jù)庫,如MySql等,但我們選擇了MongoDB,其主要原因是,相對簡單靈活且輕量,因為我們的低代碼平臺是一個專注于輔助開發(fā)的功能性平臺,非服務(wù)型產(chǎn)品,所以在數(shù)據(jù)性能方面沒有太苛刻的要求,同時,因為我們的所有開發(fā)人員為FE,考慮到”易上手“的實際因素,而nestjs對MongoDB的支持又很完善(@nestjs/mongoose),
簡述NestJs是如何運行的?
1?? 入口文件
和所有Nodejs框架一樣,NestJs也是需要一個入口文件,來完成一個“服務(wù)”的初始化,我們需要用到Nestjs為我們提供的工廠函數(shù)。于是,一個Nestjs的入口文件,大概是這樣的
import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create<NestExpressApplication>(AppModule);
// ...
// 各種express或者nest中間件
// ...
await app.listen(8001);
}
module.exports = bootstrap;
2?? 模塊加載
從上面的初始化代碼,我們可以看到,除了兩個nestjs自帶工廠函數(shù)以及泛型外,還有一個我們自己的文件“app.module”。
這里不得不說一下,Nest的本質(zhì),是一個MVC框架,而在Nestjs中,"M"更傾向于“模塊”的概念;所以,上面的初始化代碼,可以理解為整個項目的“模塊加載”,而NestJs又采用了“裝飾器”的語法來對類做修飾;下面來看一下,Nestjs如何加載全部模塊,以及在全局應(yīng)用某些邏輯
import { Module, MiddlewareConsumer, NestModule } from '@nestjs/common';
import { Controller } from './app.controller'
@Module({
imports: [
// 在這里引入其他模塊,以及數(shù)據(jù)庫相關(guān)配置
],
controllers: [Controller], // 這里可作為全局的Controller
providers: [
// 依賴注入;全局注入一些依賴邏輯,如:全局響應(yīng)攔截器等,其中依賴注入的邏輯,Nest會幫我們處理好模塊之間的依賴等關(guān)系,可以直接使用,不必手動梳理
APP_PIPE
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(
// 一些自定義的中間件,同app.use,但自定義中間件放在這里更符合開發(fā)規(guī)范
)
}
}
其中,依賴注入又是NestJs最核心的功能之一,一句話概況就是,依賴注入是代碼解耦 (Decoupling) 的一種實現(xiàn)方式,通過將依賴的 class 的構(gòu)造函數(shù)調(diào)用移動到調(diào)用方 class 外部的方式,來實現(xiàn)調(diào)用方 class 和依賴 class 解耦。因為這里面知識點比較多,且非本文重點,這里不做太多詳細介紹,有興趣的可以看一下這篇文章Nest.js 依賴注入
3?? Controller
和 MVC 中 Controller 的概念一樣;可以理解為是服務(wù)的入口和出口。一般來說都是薄薄的一層,主要起到了路由 (Routing) 的作用。當(dāng)然,其和imports中模塊內(nèi)部自身的Controller是共存的;
4?? 連接數(shù)據(jù)庫
通過上面的一個簡單介紹,大家可以了解到,一個簡單的NestJs程序是什么樣的流轉(zhuǎn)邏輯,當(dāng)然了,實際開發(fā)中,我們的模塊會非常多,這時只需要在imports中引入即可,每一個模塊(module)的結(jié)構(gòu)也是個上面提到的一樣,imports不僅可以引入其他模塊,還可以引入數(shù)據(jù)庫的配置,即該模塊需要鏈接哪幾張表。以實際代碼為例,我們來看一下如何在NestJs對應(yīng)模塊中連接MongoDB數(shù)據(jù)庫表
import { MongooseModule } from '@nestjs/mongoose';
@Module({
imports: [
MongooseModule.forFeature([
{
name: ManuScript.name,
schema: ManuScriptSchema,
collection: 't_manuscript', // 指定collection 表名稱(不指定默認加s)
},
{
name: PageGroup.name,
schema: PageGroupSchema,
collection: 't_manuscript_pagegroup',
},
],
})
其中,
- “schema”可以理解為表實例,里面描述著表結(jié)構(gòu),就像這樣
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document, Types } from 'mongoose';
export type MemoDocument = Memo & Document;
@Schema({
timestamps: true,
})
export class Memo extends Document {
@Prop({
default: Types.ObjectId,
index: true
})
memoId: string
@Prop()
designerItemId: string; // 關(guān)聯(lián)組件標識id
@Prop()
docId: string; // 關(guān)聯(lián)文稿id
}
export const MemoSchema = SchemaFactory.createForClass(Memo);
每一個@Prop下都是一個“表字段”,我們也可以將某一個字段設(shè)置為索引。
- “ collection”代表表的“表名”,我們只需要在代碼里這樣創(chuàng)建即可,不需要提前創(chuàng)建表,也不需要提前設(shè)計定義字段,最終的表現(xiàn)是這樣的
這里我們使用的“MongoDB Compass”作為MongoDB的可視化工具。
5?? 模板渲染
了解了上面的基本操作,那么你已經(jīng)可以創(chuàng)建一個可用的NestJs應(yīng)用了,對于Node來說,模板渲染能力也是其其中一個主要能力,我們利用該能力來渲染前端靜態(tài)資源,這里會在入口文件映入一個中間件,如下
app.setBaseViewsDir(join(__dirname, '..', 'views'));
app.setViewEngine('html');
app.engine('html', require('shtm').__express);
意思是,使用shtm模板渲染插件,來渲染html。同樣的,渲染模板是也是需要一個“路由”或者“Controller”的path用來指定對應(yīng)的渲染邏輯,如下
import { Controller, Get, Render } from '@nestjs/common';
@Get('/main(/*)?') // 路由,到這個路由時,執(zhí)行下面的渲染邏輯
@Render('main') // 要渲染哪個html模板(文件名)。
@ApiOperation({ summary: '首頁路由' })
main() {
return {
// 需要傳到模板中的一些數(shù)據(jù)
};
}
Nest為我們的項目落地提供了哪些便利
? 完整豐富的生態(tài)
因為是內(nèi)部創(chuàng)新型項目,好多東西都存在在未知的探索,nestjs為我們提供了強大的生態(tài)支持,為我們不斷的完善項目功能提供強大的保障;如項目中需要SCF(長鏈接)來對接公司內(nèi)部一些第三方服務(wù),我們可以通過優(yōu)秀的中間件機制輕松的完成;如項目中需要集成websocket,nest也有@nestjs/websockets這樣的能力來提供支持;如項目中,需要使用mongoose,nest也有@nestjs/mongoose這樣的能力來提供支持。這樣為我們的項目創(chuàng)新以及擴展提供了強大的技術(shù)生態(tài)保障
? 優(yōu)秀的團隊代碼管理
上面提到了,NestJs中模塊化的運行機制,這種機制下, 對于團隊開發(fā)的代碼管理具有很大的優(yōu)勢,我們可以每個人負責(zé)一個相應(yīng)的模塊(module),在代碼管理層面上,顯得非常高效
寫在后面
本文簡述了NestJs在我們內(nèi)部自研項目中的落地落地實踐經(jīng)歷,并介紹了如何創(chuàng)建一個NestJs應(yīng)用,如果看到文章的你,也有使用NodeJs搭建一個Server服務(wù)的想法,不妨試一試,NestJs吧。
