NestJS 探索與實踐
前段時間,我司前端基建團隊上線了重定向管理系統(tǒng)——它提供二維碼、短鏈接的創(chuàng)建和維護服務。
這是由前端團隊全棧開發(fā)的項目:前端使用 React,后端使用 Nestjs 開發(fā)。我們將開發(fā)過程中得到的一些實踐經(jīng)驗和思考,借著這個機會想和大家共同探討一下。也希望通過這次分享,讓大家對 NestJS 有一個初步的了解。
主題這次分享主要包含4個內容
-
什么是 nestJS?
-
應用場景和能力,能解決什么問題?
-
為什么選擇它,有什么優(yōu)勢?
-
如何上手?
What
我們先回答第一個問題:什么是 nestJS。圖中是官網(wǎng)的一段定義,大意是:
Nest 是一個用于構高效,可擴展的 Node.js 服務端應用程序的框架…… 它使用漸進式 JavaScript,內置并完全支持 TypeScript
從定義中,我們找出幾個關鍵字來看看:
-
基于 Node:對前端友好
-
服務端應用框架:主要用于服務端接口開發(fā)
-
高效,可擴展:體現(xiàn)在 Nest 各個功能模塊之間的架構是解耦的、容易進行組合的
-
漸進式:不需要一開始掌握它的全部功能特性,后續(xù)可根據(jù)業(yè)務需要逐步增加功能。
-
Nest 由 TS 開發(fā),完全支持 TS
簡單來說,Nest 是一個具有諸多特性的 Node 服務端框架。
When
第二個問題:有哪些應用場景和能力?能解決什么問題?
-
首先是最基礎的,做服務端開發(fā);
-
其次,對服務端功能的擴展,比如:安全、鑒權、隊列、日志等
-
技術架構級支持:微服務,序列化等等
以上這些場景,官方都提供成熟的解決方案,對技術選型也是一種可靠的保障。
Why
說完 Nest 的應用場景,我們再來談談:為什么選擇 Nest?我們簡單回顧一下 Node Web 框架的發(fā)展歷程,我這里將它們分為 起步和規(guī)模化 兩個階段:

起步階段 從 09 年 Node.js誕生開始,緊接著出現(xiàn) Express,Koa。它們主打輕量、極簡的框架
-
它們開發(fā)風格自由開放,導致的結果是:大家都有自己的一套開發(fā)方式(不同的分層,項目結構,文件命名)。
-
框架功能過于專一甚至簡單,團隊項目很少直接使用它們開發(fā);它們的特點好像 web 中的 JQuery,依然強大,但已經(jīng)不能滿足復雜的開發(fā)需求
在這樣的背景下,誕生了 Egg 和 Nest——主打企業(yè)級應用和團隊協(xié)作,開箱即用
Nest 與 Egg 對比
Express 和 Koa 功能比較基礎,Nest 與 Egg 是我們主要考慮的兩種方案,我們通過以下幾個方面來進行比較:

-
社區(qū)生態(tài):Nest 社區(qū)非常繁榮,由官方提供解決方案;Egg 擁有插件市集,但插件質量參差不齊
-
關注度,Nest 在 GitHub 上有 4.2 萬 star,Egg 是 1.8 萬(截止2022年1月);
-
項目更新頻率:Nest 高一些,github issue 反饋及時,使用體驗更好
-
架構設計:代碼組織方式更合理——項目結構按照功能模塊劃分;裝飾器語法更加優(yōu)雅;基于依賴注入實現(xiàn)代碼低耦合;Egg 提倡約定大于配置,缺乏一定的靈活性
-
Nest 原生支持 TS
-
上手難度:Nest 概念較多,難度稍高一點
-
維護成本,Nest 有一套標準化的開發(fā)流程,長期來看,利于保持項目的統(tǒng)一性
綜合以上幾個方面,我們認為:Nest 設計更加合理,也更適合我們團隊
去年 12月, 阿里的 D2 大會上,也推出新的 Node Web 框架:MidWay。整體設計和 Nest 比較類似:支持裝飾器,基于 DI 設計,支持 TS —— Egg.js 似乎完成了他的歷史使命,將接力棒交到了 Midway 的手中
How
接下來我們從代碼層面,介紹如何上手 Nest
腳手架

-
Nest 官方提供了腳手架,可通過 npm 全局安裝,使用方法與 Vue client 大同小異,開發(fā)體驗是比較不錯的
-
使用 Nest CLI 建立新項目非常簡單,通過
nest new xxx一鍵創(chuàng)建,會生成樣板代碼、安裝依賴 -
使用命令
npm start:dev啟動應用程序,監(jiān)聽入站 HTTP 請求。
目錄結構
初始化的項目包含一些樣板文件,主要看一下 src 目錄,里面包含了幾個核心文件

main.ts
main.ts 是程序的入口文件,它包含一個異步函數(shù):負責創(chuàng)建 app 實例,啟動、監(jiān)聽服務器。內部引入了 app.module —— 模塊文件,這是 Nest 一個核心概念
基礎概念
module
-
中文名叫模塊,在 Nest 中是可運行功能的最小單元。

-
舉個例子,我們的應用中有 用戶管理,訂單管理 等多個功能,每個功能都可獨立成一個模塊,下面這幅圖,虛線表示服務端應用,其中用戶管理功能是 UserModule,訂單管理是 OrderModule,應用則是由許多的模塊 moudule 組成……在他們前面還有一個 AppModule,是整個應用根模塊。

-
整個模塊的結構類似前端 SPA 應用:前端應用 app 掛載到 root 根節(jié)點上,pages 目錄下的文件映射為不同的頁面組件
-
為了創(chuàng)建一個基本的模塊,我們將使用 類 和 裝飾器
裝飾器

-
裝飾器是一種特殊的語法,它用來修改或增強類、函數(shù)、對象等,寫法是
@expression, 在 TS 中率先支持這種語法 -
expression表達式求值后必須為一個函數(shù),它會在運行時被調用;被裝飾的主體做為參數(shù)傳入。 -
代碼中在 AppModule 這個類前面,添加了一個
@Module()裝飾器,作用是返回一個模塊類,并提供模塊的元數(shù)據(jù)(上下文及依賴)。
app.module.ts 中引入了 Controller 和 Service ,我順著代碼的依賴關系,來介紹一下 Controller
controller

-
控制器,負責處理傳入的請求和返回的響應。
-
它像一個處理器(dispatcher),接收 HTTP 請求,然后通過 路由分發(fā) 機制,調用命中的方法
-
@Get這個請求類型裝飾器,可以接受一個路由地址。如果路由命中,則會調用getHello方法;這里為空,會匹配根路徑 -
控制器重要負責處理
HTTP請求,而將更復雜的任務委托給提供者,即 Service(providers), Service 是什么?
Service

-
在 Nest 中,服務 是一種常見的 提供者(Provider) ,它負責一些基礎、公用的方法,比如處理業(yè)務邏輯,與數(shù)據(jù)庫交互……提供者通過依賴注入的方式,被注入到控制器(Controller)中,這些方法由 Controller 調用。
剛剛介紹了Nest 中最核心的三個概念
-
模塊是 Nest 中的最小單元,許許多多的單元組成了應用;也可以看作一個容器,如下圖,每個模塊中包含了控制器和提供者

-
控制器處理
HTTP請求,分發(fā)路由;調用提供者的方法; -
提供者中定義了基礎/公共的邏輯
需要注意的是:
-
在開發(fā)中為了避免把邏輯分散在各個文件中,要注意區(qū)分控制器和提供者邊界,
即:
-
在 Controller 中主要處理請求、分發(fā)路由;注意,一個控制器中可以調用多個提供者;
-
在提供者中處理基礎業(yè)務邏輯
如下圖代碼…這樣進行功能分層,能夠保障代碼的清晰和統(tǒng)一

除此之外,Nest 還有中間件(在路由程序前后執(zhí)行),異常過濾器(處理程序拋出的異常錯誤),守衛(wèi)(權限校驗)……等概念,用于開發(fā)一些常見的功能。但我沒有把它們歸類到 Nest 核心概念中,大家有興趣下來可以再了解。
依賴注入
前面經(jīng)常講到 Nest 是可擴展,低耦合的,那么它是怎么實現(xiàn)的呢?答案是:依賴注入
依賴注入(Dependecy Injection)是實現(xiàn)低耦合的一種方式(也可以叫設計模式),它將對象創(chuàng)建和對象消耗分開。所需的依賴關系不是在內部創(chuàng)建,而是通過外部透明地傳遞
這里有一段代碼,包含兩個類:第一個是 引擎 Engine,第二個是 汽車 Car

-
Engine 有一個 cylinders 屬性,表示引擎類型,我先給他一個固定屬性為內燃機引擎
-
Car 構造器中通過 new 實例化了一個引擎,在 drive 方法中使用了引擎的類屬性,返回字符串:這是一個內燃機引擎汽車
-
有一天,我打算改動 Engine,不再設置固定類型,而是通過構造器動態(tài)傳入;那么,Car 也需要改動構造函數(shù)中的代碼。這樣一來就不符合低耦合的標準。
那我們使用依賴注入的方式,該怎么處理?

-
在汽車中,通過構造函數(shù)直接傳遞 Engine 對象,而不是在內部創(chuàng)建
-
在外部 main 方法中,實例化 Car 時,也會實例化 Engine 對象,并把 Engine 傳入到 Car 中;這個過程叫做注入;注意,這里是把 Engine 傳入到 Car 中;
-
這樣修改后,無論 Engine 的邏輯怎樣改變,都不會影響到 Car 的代碼
我們再回頭看一下,Nest 中是怎么應用依賴注入的?
-
回到 Module 文件,
@Module裝飾器中會聲明 controllers 和 providers。@Module裝飾器實際是替代了 main 函數(shù)。會實例化 Controller 和 Provider,并將 Provider 注入到 Controller 中

-
于是在 Controller 中可以通過 this 調用 service。

Nest 中是將 provider 注入到 controller,因此 Provider 的裝飾器名字叫做 Injectable
這就是在 Nest 從框架層面,通過依賴注入實現(xiàn)模塊的低耦合,從而提高了代碼的可擴展性。
Summary通過上面對 Nest 的講解,簡單總結一下:
第一:我們主要從四個方面來介紹了 Nest:What,When,Why,How
第二:Nest 存在一些缺點:它的概念較多;設計模式與平常不一樣;國內開發(fā)者目前比較少;
總體評價 Nest.js,我覺得它是一個「優(yōu)雅的,標準化,可擴展的」 Node 框架。在使用過程中除了知道這玩意怎么用,也能慢慢學習到后端開發(fā)模式,Nest 在的設計上的優(yōu)點也是值得我們學習和探索的。
作者:samwangdd
鏈接:https://juejin.cn/post/7054931414478749710
來源:稀土掘金
