[英]Confusion about when decorators are called in TypeScript
我一直认为TypeScript中的装饰器是在类的构造函数之后调用的。 但是,我被告知,例如, 这篇文章的最高答案声称在声明类时调用Decorator,而不是在实例化对象时调用 。 我注册的Angular课程的Udemy讲师也告诉我,Typescript中的装饰器在属性初始化之前运行。
但是,我在这个问题上的实验似乎表明不是这样。 例如,这是一个带有属性绑定的简单Angular代码:
test.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-test',
template: '{{testString}}'
})
export class TestComponent{
@Input() testString:string ="default string";
constructor() {
console.log(this.testString);
}
}
app.component.html
<app-test testString="altered string"></app-test>
当我执行代码时,控制台记录“默认字符串”而不是“更改字符串”。 这证明了在类的构造函数执行后调用decorator。
有人可以给我一个明确的答案,说明何时调用装饰器? 因为我在网上的研究与我做的实验相矛盾。 谢谢!
声明类时会调用装饰器 - 而不是在实例化对象时调用。
那是对的。
正如@HB已经说过的那样,我们可以通过查看已编译的代码来证明这一点。
var TestComponent = /** @class */ (function () {
function TestComponent() {
this.testString = "default string";
console.log(this.testString);
}
__decorate([
core_1.Input(),
__metadata("design:type", String)
], TestComponent.prototype, "testString", void 0);
TestComponent = __decorate([
core_1.Component({
selector: 'app-test',
template: '{{testString}}'
}),
__metadata("design:paramtypes", [])
], TestComponent);
return TestComponent;
}());
现在,让我们通过接下来的步骤来了解你的错误。
当我执行代码时,控制台记录“默认字符串”而不是“更改字符串”。 这证明了在类的构造函数执行后调用decorator。
在知道@Input()
装饰器的作用之前,你无法确定 。
Angular @Input
装饰器只是用一些信息来装饰组件属性。
它只是元数据,将存储在TestComponent.__prop__metadata__
属性中。
Object.defineProperty(constructor, PROP_METADATA, {value: {}})[PROP_METADATA]
现在是角度编译器收集有关组件的所有信息(包括@Input
metadatas)以生成组件工厂的时间。 准备好的元数据看起来像:
{
"selector": "app-test",
"changeDetection": 1,
"inputs": [
"testString"
],
...
"outputs": [],
"host": {},
"queries": {},
"template": "{{testString}}"
}
( 注意:当Angular TemplateParser遍历模板时,它使用此元数据来检查指令是否具有名称为testString
输入)
基于元数据编译器构造 updateDirective表达式:
if (dirAst.inputs.length || (flags & (NodeFlags.DoCheck | NodeFlags.OnInit)) > 0) {
updateDirectiveExpressions =
dirAst.inputs.map((input, bindingIndex) => this._preprocessUpdateExpression({
nodeIndex,
bindingIndex,
sourceSpan: input.sourceSpan,
context: COMP_VAR,
value: input.value
}));
}
将包括在生产工厂:
我们可以注意到,更新表达式是在父视图(AppComponent)中生成的。
在角度生成所有工厂并初始化所有必需的对象后,它会从顶视图到所有子视图循环更改检测。
在此过程中,angular调用checkAndUpdateView函数,它还调用updateDirectiveFn :
export function checkAndUpdateView(view: ViewData) {
if (view.state & ViewState.BeforeFirstCheck) {
view.state &= ~ViewState.BeforeFirstCheck;
view.state |= ViewState.FirstCheck;
} else {
view.state &= ~ViewState.FirstCheck;
}
shiftInitState(view, ViewState.InitState_BeforeInit, ViewState.InitState_CallingOnInit);
markProjectedViewsForCheck(view);
Services.updateDirectives(view, CheckType.CheckAndUpdate); <====
这是@Input
属性获取值的第一个地方:
providerData.instance[propName] = value;
if (def.flags & NodeFlags.OnChanges) {
changes = changes || {};
const oldValue = WrappedValue.unwrap(view.oldValues[def.bindingIndex + bindingIdx]);
const binding = def.bindings[bindingIdx];
changes[binding.nonMinifiedName !] =
new SimpleChange(oldValue, value, (view.state & ViewState.FirstCheck) !== 0);
}
正如你所看到的那样在ngOnChanges
挂钩之前发生。
Angular在装饰器执行期间不更新@Input
属性值。 变更检测机制负责此类事情。
混淆的原因不是由Decorator如何工作引起的,而是Angular如何更新它的输入绑定属性。 你可以证明自己和
ngOnInit() {
console.log(this.testString) // see updated value
}
这是因为ngOnInit
被称为第一后ngOnChanges
和ngOnChanges
更新您的输入。
您可以查看生成的代码,例如
const defaultValue = (value: any) =>
(target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
target[propertyKey] = value;
};
class Test
{
@defaultValue("steven")
myProperty: string;
constructor()
{
console.log(this.myProperty);
}
}
new Test();
会产生这个:
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var defaultValue = function (value) { return function (target, propertyKey, descriptor) { target[propertyKey] = value; }; }; var Test = /** @class */ (function () { function Test() { console.log(this.myProperty); } __decorate([ defaultValue("steven") ], Test.prototype, "myProperty", void 0); return Test; }()); new Test();
如您所见,在类声明时在属性上调用__decorate
函数。 这根据装饰器代码重新定义属性。 对于Angular,这可能只是设置一些元数据,为类输入启动。 在这里它直接设置值。
因此,此处属性已在构造函数中更改。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.