egg.js路由的优雅改造 (2)

Reflect Metadata 是 ES7 的一个提案,它主要用来在声明的时候添加和读取元数据。TypeScript 在 1.5+ 的版本已经支持它。目前JS中的装饰器更多还是对类或者类的属性进行一些操作,通过Reflect Metadata来反射获取类或者方法上的修饰器的信息

使用reflect-metadata来自定义类上的元数据,在注册路由的时候取出使用。

function classDecorator(): ClassDecorator { return target => { // 在类上定义元数据,key 为 `classMetaData`,value 为 `a` Reflect.defineMetadata('classMetaData', 'a', target); }; } function methodDecorator(): MethodDecorator { return (target, key, descriptor) => { // 在类的原型属性 'someMethod' 上定义元数据,key 为 `methodMetaData`,value 为 `b` Reflect.defineMetadata('methodMetaData', 'b', target, key); }; }

在类和类的方法上自定义完,使用 getMetadata 来取出所定义的元数据

@classDecorator() class oneClass{ @methodDecorator() otherMethod(){} } Reflect.getMetadata('classMetaData',oneClass); //返回 'a'; Reflect.getMetadata('methodMetaData',new OneClass(),'otherMehod'); //返回 'b'; egg.js controller层改造 //改造后的controller @prefix('/page') export default class PageController extends Controller { @get('/example') public async index():Promise<void> { const {ctx} = this; ctx.body={a:1}; ctx.status = 200; } }

prefix装饰器为

const CONTROLLER_PREFIX_METADATA = 'CONTROLLER_PREFIX_METADATA'; export function prefix(pathPrefix: string = ''): ClassDecorator { return (targetClass) => { //将 path的前缀路径添加到赋值到class的元数据 Reflect.defineMetadata(CONTROLLER_PREFIX_METADATA, pathPrefix, targetClass); }; }

get装饰器为

export const controllerMap = new Map<typeof Controller, typeof Controller>(); export function get(path: string = '/get') { return (target, _key, descriptor) => { //将有装饰器的controller添加到controllerMap controllerMap.set(target, target); Reflect.defineMetadata('PATH_METADATA', path, descriptor.value); Reflect.defineMetadata('METHOD_METADATA','get', descriptor.value); }; }

以此类推,可以写出POST,DELETE,PUT,HEAD,OPTIONS等http请求

将获取到的controller和路由进行注册

export enum RequestMethod { ALL = 'all', GET = 'get', POST = 'post', PUT = 'put', DELETE = 'delete', PATCH = 'patch', OPTIONS = 'options', HEAD = 'head', } export default function RouteShell(app: Application) { const { router, config } = app; controllerMap.forEach((controller: typeof Controller) => { const controllerPrefix = Reflect.getMetadata(CONTROLLER_PREFIX_METADATA, controller.constructor) || ''; Object.getOwnPropertyNames(controller).filter( (pName: string) => pName !== 'constructor' && pName !== 'pathName' && pName !== 'fullPath', ).forEach((pName: string) => { const path = Reflect.getMetadata('PATH_METADATA', controller[pName]); const method = Reflect.getMetadata('METHOD_METADATA', controller[pName]) as RequestMethod; const wrap: (this: Context, ...args: any[]) => void = async function (...args) { return new (controller.constructor as any)(this)[pName](...args); }; //路由注册 router[method](controllerPrefix + path, wrap); }); }); } 总结

经过这次对于controller层的改造,让我更加深入了解了ts中Decorator的编译产物,以及Decorator针对类的修饰和类的方法的修饰不同,进行参数传递的不同。同时使用Decorator+Reflect的方式在装饰器中能够方便简单的进行元数据的操作。并且对于egg.js的controller有了更加深入的了解,当然,现在也已经有了好几个egg-controller改造后的插件可以进行使用,虽然使用的方式各有迥异,但是其中的实现原理都是大体相同。

参考

深入理解 TypeScript- Reflect Metadata

TypeScript 中的 Decorator & 元数据反射:从小白到专家

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpjfyp.html