s 装饰器何时以及如何应用于来自 @angular 包的装饰器 类
When and how s decorator applied to the decorated classes from the @angular packages
如果我在 class 中使用装饰器,则在导入 class 时会评估装饰器。这是一个小例子:
@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
属性 中。为什么它与我的代码不同?
我问的原因是,在导入我的装饰 classes 时,我希望它应用装饰器,因此可以使用 Reflect
:
const providers = Reflect.getOwnMetadata('annotations', BModule);
但是,它不适用于 @angular
包中的装饰 classes。
当 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) 痒痒的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
这样的装饰器助手,代码将执行得更快。
为此,angular 在通过 运行 tsc 使用 --importHelpers
选项 https://github.com/angular/angular/blob/master/build.sh#L127.
构建库时使用 tslib
Angular material 做同样的事情 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;
如果我在 class 中使用装饰器,则在导入 class 时会评估装饰器。这是一个小例子:
@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
属性 中。为什么它与我的代码不同?
我问的原因是,在导入我的装饰 classes 时,我希望它应用装饰器,因此可以使用 Reflect
:
const providers = Reflect.getOwnMetadata('annotations', BModule);
但是,它不适用于 @angular
包中的装饰 classes。
当 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) 痒痒的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
这样的装饰器助手,代码将执行得更快。
为此,angular 在通过 运行 tsc 使用 --importHelpers
选项 https://github.com/angular/angular/blob/master/build.sh#L127.
tslib
Angular material 做同样的事情 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;