# 装饰器

# 类的装饰器

装饰器本身是一个函数。装饰器通过 @ 符号来使用。如果是类的装饰器,接收到的参数是一个类的构造函数:

需要打开 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'

评 论:

更新: 11/21/2020, 7:00:56 PM