[英]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.