今天的主題不是雀巢咖啡研叫,也不是雀巢奶粉,畢竟我王境澤就是餓死璧针,也不會打你們一點廣告嚷炉。
前言
近幾年由于 Node.js 的發(fā)展,JavaScript 變成了一種全棧通用語言探橱,同時誕生了諸如 Angular申屹、React、Vue 等一系列提高開發(fā)者生產(chǎn)力的優(yōu)秀前端項目框架隧膏,這些框架讓我們開發(fā)更快速哗讥、測試更便捷、拓展更簡單胞枕。
盡管 Node.js 服務(wù)端開發(fā)領(lǐng)域有著諸如 Express杆煞,Koa,F(xiàn)astify 等一系列優(yōu)秀的開源庫腐泻、工具决乎,但卻缺乏真正意義上的框架。
于是我一直在尋找派桩,油膩的師姐构诚,在哪里……
直到那個冬季,我遇見了她 —— Nest窄坦。
Nest 是什么
官方概述是這樣說的唤反。
A progressive Node.js framework for building efficient, reliable and scalable server-side applications, heavily inspired by Angular.
一個深受 Angular 啟發(fā),旨在構(gòu)建高效鸭津、可靠彤侍、高拓展性服務(wù)端應(yīng)用程序的先進(jìn)的 Node.js 框架。
正如 Nest 作者所言——深受 Angular 啟發(fā)逆趋,所以 Nest 的開發(fā)體驗與 Angular 有些類似盏阶。既可以使用 TypeScript(與 Angular 相同,官方推薦)闻书,也可以使用 JavaScript 構(gòu)建項目名斟,同時結(jié)合了面向?qū)ο缶幊蹋∣bject Oriented Programming)、函數(shù)式編程(Functional Programming)魄眉、函數(shù)響應(yīng)式編程(Functional Reactive Programming)等元素砰盐,可兼容主流第三方庫與插件。
目前 Nest 的 Github star 數(shù)在 8000 左右坑律,比我關(guān)注那會高了不少岩梳。
Nest 特點
從下圖可以看到官方宏觀概述的特點
高拓展性。可正常使用任何第三方庫冀值。
多功能性也物。適用于多種類型的服務(wù)端應(yīng)用程序編寫。
前沿先進(jìn)列疗。結(jié)合最前沿的 JavaScript 技術(shù)特性滑蚯,將設(shè)計模式和成熟方案的理念帶入 Node.js 領(lǐng)域中。
以上是較為宏觀的特性概述抵栈,關(guān)于其技術(shù)上的特點告材,我認(rèn)為較為突出的有以下幾點:
官方推薦使用 TypeScript,工程化意義重大古劲。
基于 Express 與 socket.io 套件创葡,兼容其他庫。
語法風(fēng)格類似 Angular 2+绢慢,也類似 Java Spring 框架,大量使用裝飾器(ES7 Decorator)函數(shù)洛波,無侵入式的語法使得代碼邏輯更為清晰胰舆。
遵循控制反轉(zhuǎn)(IoC, Inversion of Control)思想,大量使用依賴注入(DI, Dependency Injection)的設(shè)計模式蹬挤,大大降低了單元間的耦合度缚窿。
內(nèi)置的異常控制層(Exception Filter)焰扳,大至應(yīng)用倦零,小至邏輯,對各個級別的異常作捕獲與處理吨悍,在程序響應(yīng)上對用戶更為友好扫茅。
內(nèi)置的權(quán)限控制層(Guard)與攔截層(Interceptor)。
集成工程化的測試育瓜,官方使用 Jest 測試框架葫隙。
小試牛刀
我剛開始接觸 Nest 時,新建項目還沒有現(xiàn)在這么方便躏仇,相關(guān)依賴的安裝都比較人肉恋脚。
不過好在前不久正式版 @nestjs/cli 腳手架終于發(fā)布了(猶記得翹首以盼的我),新建項目也變得十分方便焰手。
廢話糟描,少說。腳手架书妻,走一波船响。(單押 X2)
環(huán)境準(zhǔn)備
Visual Studio Code(蘿卜青菜,各有所愛)
Node.js v9.2.0(>= 8.9.0 即可)
@nestjs/cli(官方腳手架)
$ npm i -g @nestjs/cli
· nest-demo(項目初始化)
$ nest new nest-demo
初識 Nest
如無意外,通過腳手架初始化項目的過程大致如下
?? Creating your Nest project...
?? We have to collect additional information:
? description : description
? version : 0.0.0
? author : chenshihao
?? Thank you for your time!
CREATE /nest-demo/.prettierrc (51 bytes)
CREATE /nest-demo/README.md (339 bytes)
CREATE /nest-demo/nodemon.json (147 bytes)
CREATE /nest-demo/package.json (1527 bytes)
CREATE /nest-demo/src/app.controller.spec.ts (588 bytes)
CREATE /nest-demo/src/app.controller.ts (266 bytes)
CREATE /nest-demo/src/app.module.ts (249 bytes)
CREATE /nest-demo/src/app.service.ts (138 bytes)
CREATE /nest-demo/src/main.hmr.ts (329 bytes)
CREATE /nest-demo/src/main.ts (208 bytes)
CREATE /nest-demo/test/app.e2e-spec.ts (593 bytes)
CREATE /nest-demo/test/jest-e2e.json (154 bytes)
CREATE /nest-demo/tsconfig.json (477 bytes)
CREATE /nest-demo/tslint.json (895 bytes)
CREATE /nest-demo/webpack.config.js (695 bytes)
CREATE /nest-demo/.nestcli.json (60 bytes)
? Which package manager would you ?? to use? yarn
????? Take ?? or ?? during the packages installation process and enjoy your time
?? Successfully created project nest-demo
以上操作創(chuàng)建了種子項目并用 yarn 安裝了依賴包灿意。如果沒有安裝成功的話估灿,可手動進(jìn)行安裝。
我們可以看到缤剧,腳手架工具創(chuàng)建了若干文件馅袁,包括
-
根目錄
大多為配置文件,如 prettier荒辕、nodemon汗销、tslint 等工具的配置文件(若不熟悉可以暫時忽略,將重心放在 *.ts 文件上有助于快速入手框架)
-
test 文件夾
端對端(e2e)測試用例與配置文件
-
src 文件夾
主程序入口文件及若干模塊文件抵窒。其中文件層次關(guān)系大致如下
我們對文件結(jié)構(gòu)有了初步的認(rèn)識
入口文件 main.ts 引導(dǎo)程序運行弛针,加載模塊 app.module.ts
模塊中,包括控制器 app.controller.ts李皇,服務(wù)提供商 app.service.ts削茁,測試用例 app.controller.spec.ts 等組件。
接下來我們直接以開發(fā)模式運行程序掉房。
$ npm run start:dev
如無意外减余,我們可以看到控制臺有以下輸出日志蔓姚。
> nest-demo@0.0.0 start:dev /Users/victor/Desktop/nest-demo
> nodemon
[nodemon] 1.18.3
[nodemon] to restart at any time, enter `rs`
[nodemon] watching: /Users/victor/Desktop/nest-demo/src/**/*
[nodemon] starting `ts-node -r tsconfig-paths/register src/main.ts`
[Nest] 16999 - 2018-8-20 11:33:49 [NestFactory] Starting Nest application...
[Nest] 16999 - 2018-8-20 11:33:49 [InstanceLoader] AppModule dependencies initialized +8ms
[Nest] 16999 - 2018-8-20 11:33:49 [RoutesResolver] AppController {/}: +14ms
[Nest] 16999 - 2018-8-20 11:33:49 [RouterExplorer] Mapped {/, GET} route +3ms
[Nest] 16999 - 2018-8-20 11:33:49 [NestApplication] Nest application successfully started +3ms
從圖中我們能夠得到的信息大致為
運行 nodemon 監(jiān)聽項目文件
通過 ts-node 引導(dǎo)入口文件 main.ts
初始化模塊與路由
在 main.ts 可以看到览妖,默認(rèn)端口號為 3000
main.ts
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
我們直接訪問地址 http://localhost:3000 看看效果
$ curl localhost:3000
Hello World!
樣例不作為參考站蝠,正式開發(fā)中,推薦使用 Postman 進(jìn)行 API 調(diào)試
Hello World!
看到 “Hello World! ” 的字符串哪亿,不禁讓猿熱血澎湃 —— 他做到了粥烁!
我們回過頭來看看這個值是怎么來的。
通過結(jié)構(gòu)示例圖蝇棉,我們知道模塊 (module) 是一個非常重要的概念讨阻,而每個模塊的控制器 (controller) 更是扮演著運籌帷幄的角色。
app.controller.ts
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
root(): string {
return this.appService.root();
}
}
app.controller.ts 文件中定義了一個類银萍,通過裝飾器 @Controller() 使其成為一個路由控制類变勇。類方法中,有構(gòu)造函數(shù) constructor 以及帶有裝飾器 @Get() 的 root 函數(shù)贴唇。
app.service.ts
@Injectable()
export class AppService {
root(): string {
return 'Hello World!';
}
}
root 函數(shù)通過調(diào)用 service 中的 root 方法搀绣,返回字符串 “Hello World!”。
此外戳气,關(guān)于 HTTP 響應(yīng)的操作链患,Nest 大體上已經(jīng)幫我們安排的明明白白了,這也是官方推薦的寫法瓶您。
當(dāng)我們的函數(shù)返回 JavaScript 對象或數(shù)組時麻捻,返回值會被自動轉(zhuǎn)化成 JSON 對象纲仍;
當(dāng)我們的函數(shù)返回字符串時,返回值不作處理贸毕。
響應(yīng)狀態(tài)碼默認(rèn)情況下總是 200郑叠,除了 POST 請求為 201 外,當(dāng)然明棍,我們是有辦法通過裝飾器輕松修改返回值的乡革。
當(dāng)然啦,如果有倔強的老哥非要操作一下 response 對象摊腋,也不是不可以滴沸版。
讀到這里,我們便大致了解能用 GET 方法訪問 http://localhost:3000 得到“Hello World!”的來龍去脈了吧兴蒸。
裝飾器
在上述代碼片段中视粮,我們會發(fā)現(xiàn)許多裝飾器,如 @Controller()橙凳、@Injectable() 等蕾殴。
裝飾器是什么?裝飾器的定義大致如下
An ES2016 decorator is an expression which returns a function and can take a target, name and property descriptor as arguments. You apply it by prefixing the decorator with an @ character and placing this at the very top of what you are trying to decorate. Decorators can be defined for either a class or a property.
ES7 裝飾器是一種返回函數(shù)岛啸,且可傳遞目標(biāo)對象区宇、名稱與屬性描述作為參數(shù)的表達(dá)式。你可以使用@字符作為前綴并將裝飾器放在你想裝飾的對象上值戳。裝飾器可以被用在一個類或者一個屬性上。
想要了解更多關(guān)于裝飾器的知識炉爆,可以參考這篇文章或者自行搜索
https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841
裝飾器 (Decorator) 作為 ES7 的一大特性堕虹,在 TypeScript 中可以被自由運用,在 Nest 中更是得到了合理的開發(fā)與利用芬首。
縱觀 Nest 赴捞,裝飾器貫穿了整個 Nest 框架,如:
模塊郁稍,一個帶有 @Module() 裝飾器的類
控制器赦政,一個帶有 @Controller() 裝飾器的類
提供商,一個帶有 @Injectable() 裝飾器的類
通過裝飾器語法裝飾參數(shù)耀怜,更清晰便捷地取值
為了更好地說明裝飾參數(shù)恢着,我們舉個栗子,修改一下 app.controller.ts 和 app.service.ts
app.controller.ts
import { Query } from '@nestjs/common';
@Get()
root(@Query('name') name: string = 'Victor'): string {
return this.appService.root(name);
}
往 app.controller.ts 的 GET 方法中添加參數(shù) name财破,其中 Query 指的是 url 中的 params 參數(shù)
app.service.ts
@Injectable()
export class AppService {
root(name: string = 'World'): string {
return `Hello ${name}!`;
}
}
配合控制層掰派,往 app.service.ts 添加參數(shù) name 以說明值的改變。
此時服務(wù)會自動重啟左痢,我們以 GET 方式請求新地址
http://localhost:3000?name=victor
$ curl http://localhost:3000?name=Victor
Hello Victor!
Surprise靡羡!好了系洛,一個 url 傳參的鮮活栗子就這么輕松加愉快的舉完了。
除了 @Query() 之外略步,Nest 內(nèi)置的還有一些裝飾器提供給我們直接使用描扯,列舉一下
@Request() —— Express 請求對象
@Response() —— Express 響應(yīng)對象
@Session() —— Session 對象
@Param(param?: string) —— RESTful 風(fēng)格的路由參數(shù)
@Body(param?: string) —— 請求體
@Headers(param?: string) —— 請求頭
官方提供的裝飾器固然方便,然鵝在錯綜復(fù)雜的需求下趟薄,僅僅有這幾個裝飾器還是略顯不足的绽诚。在這里,我們甚至可以自定義裝飾器竟趾。
這里我們新建一個文件 name.decorator.ts憔购,為了避免邏輯干擾,在這里我們直接返回傳遞的值 data岔帽。
name.decorator.ts
import { createParamDecorator } from '@nestjs/common';
export const Name = createParamDecorator((data, req) => {
return data;
});
在 controller 中引用自定義裝飾器玫鸟,并傳入常量 Victor。
app.controller.ts
import { Name } from 'name.decorator';
@Get()
root(@Name('Victor') name: string): string {
return this.appService.root(name);
}
此時服務(wù)重啟犀勒,再次訪問 http://localhost:3000屎飘。
$ curl localhost:3000
Hello Victor!
通過裝飾器的作用,我們成功的將 name 的值設(shè)置成 Victor贾费,這是一個簡單的例子钦购,在實際生產(chǎn)環(huán)境中可以添加更為復(fù)雜的邏輯。
依賴注入
如果說裝飾器語法是 Nest 健美的身材褂萧,那么依賴注入則可以稱為 Nest 有趣的靈魂了押桃。
簡單來說,依賴注入(DI, Dependency Injection)是一種設(shè)計模式导犹,旨在提供對象實例唱凯,調(diào)用單元在使用依賴對象實例時無需關(guān)心其提供方式,而是統(tǒng)一由 DI 系統(tǒng)提供谎痢。
想要了解更多依賴注入知識磕昼,可以參考以下文章或者自行搜索
https://angular.io/guide/dependency-injection-pattern
事實上,Nest 的設(shè)計理念是节猿,“萬物皆可為提供商” —— 如服務(wù)類(service)票从、倉庫類(repository)、工廠類(factory)滨嘱、幫助類(helper)等等峰鄙。
我們從實際代碼入手了解這一概念,前面提到的 @Injectable() 裝飾器太雨,其裝飾的對象正是我們所說的“依賴對象”先馆,也就是例子中的 service 類。
app.module.ts
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
在 module 文件中躺彬,我們聲明了 AppService 類作為 provider煤墙,這是 AppController 可以無實例化直接使用 appService 對象的伏筆梅惯。
app.controller.ts
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
root(): string {
return this.appService.root();
}
}
AppService 作為可注入的依賴對象,通過在構(gòu)造函數(shù) constructor 中聲明的方式仿野,配合 DI 模式铣减,解決了兩者之間的依賴關(guān)系與獨立性。于是脚作,我們可以直接在方法中調(diào)用 appService葫哗。
app.service.ts
@Injectable()
export class AppService {
root(): string {
return 'Hello World!';
}
}
理解了依賴注入的概念,可以更好的閱讀與學(xué)習(xí) Nest 的其他內(nèi)容球涛,如中間件(Middlewire)劣针、管道(Pipe)、防御層(Guard)亿扁、攔截器(Interceptor)等等捺典,都是通過依賴注入的方式與我們的控制層進(jìn)行關(guān)聯(lián)的。
實際上从祝,配合 @Inject() 裝飾器襟己,可以實現(xiàn)更為多樣的注入類型,這是更為高級的 provider 注入方式牍陌,由于文章篇幅原因擎浴,在此就不贅述了。
結(jié)束語
這是我的第一篇公眾號文章毒涧。
初心是為了介紹 Nest 框架贮预,希望是不僅有落到實處的代碼示例,不至于顯得太空虛契讲;同時也有更為重要的核心概念介紹萌狂,讓讀者有更好的大局觀與學(xué)習(xí)方向。
一篇文章往往不足以描述事物的全部怀泊,還有很多優(yōu)秀的思想與巧妙的設(shè)計等著我們?nèi)ヌ剿鳎覀兿缕恼乱姡?/p>
參考資料
原創(chuàng)作者
陳仕豪误趴,年十八霹琼,騷話連篇笑哈哈。skr~