TypeScriptのデコレータは、クラスのコンストラクタの後に呼び出されるという印象を受けました。ただし、別の方法で言われました。たとえば、 this postのトップアンサーは、オブジェクトがインスタンス化されたときではなく、クラスが宣言されたときにデコレータが呼び出されると主張しています。私が登録したAngularコースのUdemyインストラクターは、TypeScriptのデコレータがbeforeプロパティの初期化を実行することも教えてくれました。
しかし、この主題に関する私の実験はそうではないことを示しているようです。たとえば、これは単純な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>
コードを実行すると、コンソールは「変更された文字列」ではなく「デフォルトの文字列」をログに記録します。これは、クラスのコンストラクターが実行された後にデコレーターが呼び出されることを証明しています。
デコレータがいつ呼び出されるかについて、誰かが明確な答えを教えてもらえますか?オンラインでの私の調査は、私が行った実験と矛盾しているためです。ありがとうございました!
デコレータは、オブジェクトがインスタンス化されたときではなく、クラスが宣言されたときに呼び出されます。
そのとおりです。
@ H.Bとしてすでに述べたように、トランスパイルされたコードを見ることでそれを証明することができます。
_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;
}());
_
それでは、次の手順を実行して、どこが間違っていたかを理解しましょう。
コードを実行すると、コンソールは「変更された文字列」ではなく「デフォルトの文字列」をログに記録します。これは、クラスのコンストラクターが実行された後にデコレーターが呼び出されることを証明しています。
@Input()
デコレータが何をするかを知るまでは確信が持てません。
Angular _@Input
_デコレータは、いくつかの情報を含むコンポーネントプロパティを装飾します。
これは単なるメタデータであり、_TestComponent.__prop__metadata__
_プロパティに stored になります。
_Object.defineProperty(constructor, PROP_METADATA, {value: {}})[PROP_METADATA]
_
angularコンパイラが_@Input
_メタデータを含むコンポーネントに関するすべての情報を収集して、コンポーネントファクトリを生成するときです。準備されたメタデータは次のようになります。
_{
"selector": "app-test",
"changeDetection": 1,
"inputs": [
"testString"
],
...
"outputs": [],
"Host": {},
"queries": {},
"template": "{{testString}}"
}
_
(注:Angular TemplateParser テンプレートをウォークスルーすると、このメタデータが使用されます チェックする ディレクティブにtestString
という名前の入力があるかどうか)
メタデータコンパイラに基づく constructs 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がすべてのファクトリを生成し、必要なすべてのオブジェクトを初期化した後、上面図からすべての子ビューまで変化検出サイクルを実行します。
このプロセス中にangular呼び出し checkAndUpdateView 関数、ここでも pdateDirectiveFn :
_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
_プロパティ値を更新しません。変化検出メカニズムはそのようなことを担当します。
混乱の理由は、デコレータの動作ではなく、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の場合、これはおそらくメタデータを設定し、入力用にクラスをプライミングします。ここでは、値を直接設定します。
したがって、ここでは、プロパティはコンストラクタですでに変更されています。