❝本項目源碼地址:https://github.com/pingan8787/Leo-JavaScript/blob/master/Cute-Gist/LearnSource/OvernightDemo/html
❞
隨着 Nodejs 在前端涉及領域愈來愈廣,也愈來愈成熟,相信不少朋友已經嘗試或使用過 Nodejs 開發服務端項目了。 前端
本文我將和你們一塊兒回顧 Express,而後介紹一個超級外掛——「OvernightJS」,它強大的地方在於,它將爲 Express 路由提供 TypeScript 裝飾器支持,使得咱們開發路由更加簡單,代碼複用性更好。 node
這裏也但願幫助你們對 TypeScript 的裝飾器有更深瞭解。git
接下來跟本文主角 Leo 一塊兒來看看這個外掛吧~es6
1、背景介紹最近 Leo 打算使用 Express 來開始重構本身博客的服務端項目,通過認真研究和設計,並肯定完方案,Leo 開始下手啦:github
// app.ts
import express, { Application, Request, Response } from 'express';
const app: Application = express();
app.get('/', (req: Request, res: Response) => {
res.send('Hello World!');
});
app.listen(3000, ()=> {
console.log('Example app listening on port 3000!');
});
其中 tsconfig.json 配置以下:express
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"experimentalDecorators": true, // 開啓裝飾器
"emitDecoratorMetadata": true, // 開啓元編程
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
基本代碼寫完,測試能不能跑起來。 編程
Leo 在命令行使用 ts-node
命令行執行。(ts-node 用來直接運行 ts 文件,詳細介紹請查看文檔,這裏不細講咯):json
$ ts-node app.ts
看到命令行輸出:api
Example app listening on port 3000!
服務跑起來了,心情愉快。
接下來 Leo 使用 Express 的路由方法寫了其餘接口:
// app.ts
app.get('/article', (req: Request, res: Response) => {res.send('Hello get!')});
app.post('/article', (req: Request, res: Response) => {res.send('Hello post!')});
app.put('/article', (req: Request, res: Response) => {res.send('Hello put!')});
app.delete('/article', (req: Request, res: Response) => {res.send('Hello delete!')});
app.get('/article/list', (req: Request, res: Response) => {res.send('Hello article/list!')});
// ... 等等其餘接口
❝Express 路由方法派生自 HTTP 方法之一,附加到 express 類的實例。 支持對應於 HTTP 方法的如下路由方法:get、post、put、head、delete、options等等。
❞
同事 Robin 看了看代碼,問到:
隨着接口越寫越多,代碼難免出現複雜和冗餘的狀況,爲了解決這個問題,Leo 引入 Express 的 Router()
,來建立「可安裝的模塊化路由處理程序」。Router 實例是「完整的中間件」和「路由系統」。所以,經常將其稱爲「「微型應用程序」」。
Leo 新建文件 app.router.ts
,從新實現上面接口:
// app.router.ts
import express, { Router, Request, Response } from 'express';
const router: Router = express.Router();
router.get('/', (req: Request, res: Response) => {res.send('Hello get!')});
router.post('/', (req: Request, res: Response) => {res.send('Hello post!')});
router.put('/', (req: Request, res: Response) => {res.send('Hello put!')});
router.delete('/', (req: Request, res: Response) => {res.send('Hello delete!')});
router.get('/user', (req: Request, res: Response) => {res.send('Hello api/user!')});
export default router;
接着在 app.ts 中使用,因爲express.Router()
是個中間件,所以可使用 app.use()
來使用:
// app.ts
// 刪除原來路由聲明
import router from "../controller/app.router";
app.use('/api', router);
這裏 app.use
第一個參數 /api
表示這一組路由對象的根路徑,第二個參數 router
表示一組路由對象。
因而就實現了下面 API 接口:
/api
/api/user
肯定全部接口正常運行後,Leo 琢磨着,既然 Express 每一個路由都是由「路由名稱」和「路由處理方法」組成,那爲何不能給 Express 加個外掛?爲每一個路由添加裝飾器來裝飾。
幸運的是,已經有大佬實現這個外掛了,它就是今天主角——OvernightJS。
2、基礎知識介紹在開始介紹 Overnight 以前,咱們先回顧下「裝飾器」和「Reflect」:
TypeScript 中,裝飾器(Decorators)是一種特殊類型的聲明,它可以被附加到類聲明、方法、訪問符、屬性或參數上,「本質上仍是個函數」。
裝飾器爲咱們在類的聲明及成員上經過「元編程語法」添加標註提供了一種方式。
須要記住這幾點:
target
、name
和 descriptor
;descriptor
對象,用於配置 target
對象;更多裝飾器詳細介紹,請閱讀文檔《TypeScript 裝飾器》(https://www.tslang.cn/docs/handbook/decorators.html)。
裝飾器通常包括:
這裏以類裝飾器(Class decorators)爲例,介紹如何使用裝飾器:
function MyDecorators(target: Function): void {
target.prototype.say = function (): void {
console.log("Hello 前端自習課!");
};
}
@MyDecorators
class LeoClass {
constructor() {}
say(){console.log("Hello Leo")}
}
let leo = new LeoClass();
leo.say();
// 'Hello Leo!';
裝飾器實際上很是簡單,編譯出來之後,只是個函數,咱們接着看。
這裏以《1.3 示例代碼》爲例,看看它的編譯結果:
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
function MyDecorators(target) {
target.prototype.say = function () {
console.log("Hello 前端自習課!");
};
}
let LeoClass = class LeoClass {
constructor() { }
say() { console.log("Hello Leo"); }
};
LeoClass = __decorate([
MyDecorators,
__metadata("design:paramtypes", [])
], LeoClass);
let leo = new LeoClass();
leo.say();
// 'Hello Leo!';
其實就是 __decorate
函數啦,具體你們能夠自行細看咯~
從編譯後 JS 代碼中能夠看出,「裝飾器是在模塊導入時便執行的」。以下:
LeoClass = __decorate([
MyDecorators,
__metadata("design:paramtypes", [])
], LeoClass);
接下來經過下圖來回顧裝飾器的知識。
Reflect(即反射)是 ES6 新增的一個「內置對象」,它提供用來「攔截和操做」 JavaScript 對象的 API。而且 「Reflect 的全部屬性和方法都是靜態的」,就像 Math 對象( Math.random()
等)。
更多 Reflect 詳細介紹,請閱讀文檔《MDN Reflect》(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Reflect)。
其核心目的,是爲了保持 JS 的簡單,讓咱們能夠不用寫不少代碼,這裏舉個栗子