# 装饰器
# 类的装饰器
装饰器本身是一个函数。装饰器通过 @ 符号来使用。如果是类的装饰器,接收到的参数是一个类的构造函数:
需要打开 tsconfig.json 中的 experimentalDecorators 、emitDecoratorMetadata 的注释,开启对修饰器的实验支持;
只要类被定义,装饰器就会执行;也就是说他就执行一次;
function testDecorator(constructor: any) {
console.log('decorator')
}
@testDecorator
class Test {}
const test = new Test()
也可以使用多个装饰器;装饰器执行的顺序是从下到上,从右到左的顺序。
function testDecorator(constructor: any) {
constructor.prototype.getName = () => {
console.log('decorator')
}
}
function testDecorator1(constructor: any) {
constructor.prototype.getName = () => {
console.log('decorator')
}
}
@testDecorator
@testDecorator1
class Test {}
const test = new Test()
如果想要在装饰器中添加一些判断,可以将装饰器写成一个函数,然后执行:
function testDecorator(flag: boolean) {
if (flag) {
return function (constructor: any) {
constructor.prototype.getName = () => {
console.log('decorator')
}
}
} else {
return function (constructor: any) {}
}
}
@testDecorator(true)
class Test {}
const test = new Test()
# 使用函数装饰器
安装依赖: npm install reflect-metadata --save;然后在项目中直接引入:import 'reflect-metadata', reflect-metadata 这个就是诸如 C#(.NET)和 Java 之类的语言支持将元数据添加到类型的属性或注释,以及用于读取元数据的反射 API; 使用如下:
import 'reflect-metadata'
// 存入元数据
Reflect.defineMetadata('method', type, target, key)
// 取出
Reflect.getMetadata('method', target.prototype, key)
我们借助函数装饰器以及类装饰器进行优化代码,之前我们注册路由是通过 router 文件,然后在 index.ts 中进行使用:
// index.ts
import router from './router'
app.use(router)
// router.ts
import { Router, Request, Response, NextFunction } from 'express'
const router = Router()
// express 库的类型定义文件 .d.ts 文件类型描述不准确
interface BodyRequest extends Request {
body: {
[key: string]: string | undefined
}
}
router.get('/', (req: BodyRequest, res: Response) => {
res.send()
})
router.get('/logout', (req: BodyRequest, res: Response) => {
res.json()
})
一般的后台结构为 mvc 三层结构,我们修改我们代码的结构,在 src 新增 controller 专门来处理路由相关的方法:
import { Request, Response } from 'express'
import 'reflect-metadata'
import { getResponseData } from '../utils/utils'
import { controller, get, post } from './decorator'
interface BodyRequest extends Request {
body: {
[key: string]: string | undefined
}
}
@controller
class LoginController {
@post('/login')
login(req: BodyRequest, res: Response) {}
@get('/logout')
logout(req: BodyRequest, res: Response) {}
}
我们通过在方法的装饰器中进行定义他的 path 以及 访问的方法,然后在类的装饰器中进行获取到元数据再进行注册路由:
方法装饰器 get 、post
function getRequestDecorator(type: string) {
return function (path: string) {
return function (target: any, key: string) {
Reflect.defineMetadata('path', path, target, key)
// 存入值
Reflect.defineMetadata('method', type, target, key)
}
}
}
export const get = getRequestDecorator('get')
export const post = getRequestDecorator('post')
类的装饰器:根据不同的 type 来注册不同的路由
export function controller(target: any) {
for (let key in target.prototype) {
const path = Reflect.getMetadata('path', target.prototype, key)
const method: Method = Reflect.getMetadata('method', target.prototype, key)
const handle = target.prototype[key]
if (path && method && handle) {
router[method](path, handle)
// router.get(path, handle)
}
}
}
然后我们在程序的入口 index.ts 文件中,进行使用;首先我们需要执行一次我们的控制器文件,直接在 index.ts 中引入:
// 自动执行类,只需要引用即可
import './controller/LoginController'
// 在控制器注册的路由
import { router } from './controller/decorator'
阅读量: