装饰器何时以及如何应用到 @angular 包中的装饰类

Posted

技术标签:

【中文标题】装饰器何时以及如何应用到 @angular 包中的装饰类【英文标题】:When and how s decorator applied to the decorated classes from the @angular packages 【发布时间】:2017-12-29 16:22:37 【问题描述】:

如果我在我的类中使用装饰器,则在导入类时会评估装饰器。这是一个小例子:

@NgModule( ... )
export class BModule  ... 

转译为:

var BModule = (function () 
    function BModule() 
    
    BModule = __decorate([  <---------- decorators are applied here
        core_1.NgModule(...)
    ], BModule);
    return BModule;
());
exports.BModule = BModule;

但是,当在 @angular 包中应用模块或任何其他装饰器时,输出如下:

var HttpClientModule = (function () 
    function HttpClientModule() 
    
    return HttpClientModule;
());
HttpClientModule.decorators = [
     type: _angular_core.NgModule, args: [ ... ,] ,
];

如您所见,这里没有应用装饰器。它们只是保存在decorators 属性中。为什么它与我的代码不同?

我问的原因是,在导入我的装饰类时,我希望它应用装饰器,因此可以使用 Reflect

const providers = Reflect.getOwnMetadata('annotations', BModule);

但是,它不适用于 @angular 包中的装饰类。

【问题讨论】:

【参考方案1】:

当 angulat 解析注释时,它有 three options:

1) 直接 API

// Prefer the direct API.
if ((<any>typeOrFunc).annotations && (<any>typeOrFunc).annotations !== parentCtor.annotations) 
  let annotations = (<any>typeOrFunc).annotations;
  if (typeof annotations === 'function' && annotations.annotations) 
    annotations = annotations.annotations;
  
  return annotations;

我们在 ES5 中写代码时通常使用这个 API

MyComponent.annotations = [
  new ng.Component(...)
]

2) tsickle 的 API

// API of tsickle for lowering decorators to properties on the class.
if ((<any>typeOrFunc).decorators && (<any>typeOrFunc).decorators !== parentCtor.decorators) 
  return convertTsickleDecoratorIntoMetadata((<any>typeOrFunc).decorators);

这样 Angular 从@angular/(core|material...) 库中读取注释。 Angular 以这种方式编译库是因为它有助于优化包。例如,我们不需要像_decorate, __metadata 那样发送装饰器助手,代码会执行得更快。

对于该角度使用tslib 构建库时通过运行带有--importHelpers 选项https://github.com/angular/angular/blob/master/build.sh#L127 的tsc。

Angular 材质做同样的事情https://github.com/angular/material2/blob/master/tools/package-tools/rollup-helpers.ts#L9-L11

// Import tslib rather than having TypeScript output its helpers multiple times.
// See https://github.com/Microsoft/tslib
'tslib': 'tslib',

3) 使用反射

// API for metadata created by invoking the decorators.
if (this._reflect && this._reflect.getOwnMetadata) 
  return this._reflect.getOwnMetadata('annotations', typeOrFunc);

当我们使用 typescript 发出的元数据时使用此 API

为确保您能正确获取元数据,您可以考虑使用如下函数:

declare let Reflect: any;
function getAnnotations(typeOrFunc: Type<any>): any[]|null 
  // Prefer the direct API.
  if ((<any>typeOrFunc).annotations) 
    let annotations = (<any>typeOrFunc).annotations;
    if (typeof annotations === 'function' && annotations.annotations) 
      annotations = annotations.annotations;
    
    return annotations;
  

  // API of tsickle for lowering decorators to properties on the class.
  if ((<any>typeOrFunc).decorators) 
    return convertTsickleDecoratorIntoMetadata((<any>typeOrFunc).decorators);
  

  // API for metadata created by invoking the decorators.
  if (Reflect && Reflect.getOwnMetadata) 
    return Reflect.getOwnMetadata('annotations', typeOrFunc);
  
  return null;


function convertTsickleDecoratorIntoMetadata(decoratorInvocations: any[]): any[] 
  if (!decoratorInvocations) 
    return [];
  
  return decoratorInvocations.map(decoratorInvocation => 
    const decoratorType = decoratorInvocation.type;
    const annotationCls = decoratorType.annotationCls;
    const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : [];
    return new annotationCls(...annotationArgs);
  );



const annotations = getAnnotations(AppModule);

更新:

通过调用装饰器创建的元数据 API 在 5.0.0-beta.4 中已更改

const ANNOTATIONS = '__annotations__';

// API for metadata created by invoking the decorators.

if (typeOrFunc.hasOwnProperty(ANNOTATIONS)) 
   return (typeOrFunc as any)[ANNOTATIONS];

return null;

【讨论】:

感谢您的回答)也许可以添加一些指向源的链接?此外,似乎我们可以依赖 decorators 的存在,直到支持 tsickle,对吗?我展示了需要decorators 属性的解决方案here 我认为一旦浏览器原生支持 Reflect 和 Decorators,前两个选项将不被支持 添加了链接。我认为在这种情况下我们可以使用 Reflect 作为后备 @Maximus 以防我们不知道以哪种格式构建库。这就是 Angular 使用所有三个选项的原因 很遗憾,Reflect 似乎没有从 .decorators 属性中检索元数据

以上是关于装饰器何时以及如何应用到 @angular 包中的装饰类的主要内容,如果未能解决你的问题,请参考以下文章

Angular 中的 @Directive 与 @Component

Angular 中的 @Directive 与 @Component

Angular 中的 @Directive 与 @Component

Angular 中的 @Directive 与 @Component

Angular 中的 @Directive 与 @Component

将装饰器应用于python包中的所有函数