❝本项目源码地址: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 的简单,让咱们能够不用写不少代码,这里举个栗子