Lassen Sie uns über Metadaten und Dekoratoren in Angular sprechen
Feb 28, 2022 am 11:10 AMDieser Artikel setzt das Erlernen von Angular fort, führt Sie zum Verst?ndnis der Metadaten und Dekoratoren in Angular und erkl?rt kurz deren Verwendung. Ich hoffe, dass er für alle hilfreich ist!
Als Front-End-Framework, das ?für gro?e Front-End-Projekte“ entwickelt wurde, verfügt Angular tats?chlich über viele Designs, die es wert sind, erw?hnt und erlernt zu werden. Diese Serie wird haupts?chlich zum Studium der Implementierungsprinzipien dieser Designs und Funktionen verwendet. In diesem Artikel werden haupts?chlich Metadaten vorgestellt, die überall in Angular zu finden sind. [Empfohlene verwandte Tutorials: ?Angular-Tutorial“]
Dekoratoren sind das Kernkonzept bei der Entwicklung mit Angular. In Angular werden Dekoratoren verwendet, um Metadaten an Klassen oder Eigenschaften anzuh?ngen, damit Sie wissen, was diese Klassen oder Eigenschaften bedeuten und wie Sie mit ihnen umgehen.
Dekoratoren und Metadaten
Weder Dekoratoren noch Metadaten sind von Angular vorgeschlagene Konzepte. Werfen wir also zun?chst einen kurzen Blick darauf.
Metadaten
In einem g?ngigen Konzept sind Metadaten Daten, die Benutzerdaten beschreiben. Es fasst grundlegende Informationen zu den Daten zusammen und kann das Auffinden und Verwenden bestimmter Dateninstanzen erleichtern. Beispielsweise sind Autor, Erstellungsdatum, ?nderungsdatum und Dateigr??e Beispiele für sehr grundlegende Dokumentmetadaten.
Im Fall von Klassen werden Metadaten verwendet, um die Klasse zu dekorieren, um die Definition und das Verhalten der Klasse zu beschreiben, sodass das erwartete Verhalten der Klasse konfiguriert werden kann.
Decorator
Decorator ist eine Sprachfunktion von JavaScript und eine experimentelle Funktion in Stufe 2.
Dekoratoren sind Funktionen, die w?hrend der Definition einer Klasse, eines Klassenelements oder einer anderen JavaScript-Syntaxform aufgerufen werden.
Dekoratoren haben drei Hauptfunktionen:
k?nnen den zu dekorierenden Wert durch einen passenden Wert mit derselben Semantik ersetzen. (Ein Dekorateur kann beispielsweise eine Methode durch eine andere Methode, ein Feld durch ein anderes Feld, eine Klasse durch eine andere Klasse usw. ersetzen.)
Es ist m?glich, Metadaten mit dem ge?nderten Wert zu verknüpfen; diese Metadaten k?nnen extern gelesen und für die Metaprogrammierung und Selbstprüfung verwendet werden.
Der Zugriff auf den zu ?ndernden Wert kann über Metadaten erfolgen. Bei ?ffentlichen Werten erfolgt dies über den Wertnamen, bei privaten Werten erhalten sie Zugriffsfunktionen, die dann optional geteilt werden k?nnen.
Im Wesentlichen k?nnen Dekoratoren verwendet werden, um einen Wert zu metaprogrammieren und ihm Funktionalit?t hinzuzufügen, ohne sein ?u?eres Verhalten grundlegend zu ?ndern.
Weitere Inhalte finden Sie im tc39/proposal-decorators-Vorschlag.
Dekoratoren und Metadaten in Angular
Wenn wir Angular-Anwendungen entwickeln, seien es Komponenten, Anweisungen, Dienste, Module usw., müssen wir Dekoratoren verwenden, um sie zu definieren und zu entwickeln. Dekoratoren erscheinen unmittelbar vor einer Klassendefinition und werden verwendet, um zu deklarieren, dass die Klasse einen bestimmten Typ hat, und um für diesen Typ geeignete Metadaten bereitzustellen.
Zum Beispiel k?nnen wir die folgenden Dekoratoren verwenden, um Angular-Klassen zu deklarieren: @Component()
, @Directive()
, @Pipe()
, @Injectable()
, @NgModule()
. @Component()
、@Directive()
、@Pipe()
、@Injectable()
、@NgModule()
。
使用裝飾器和元數(shù)據(jù)來(lái)改變類(lèi)的行為
以@Component()
為例,該裝飾器的作用包括:
將類(lèi)標(biāo)記為 Angular 組件。
提供可配置的元數(shù)據(jù),用來(lái)確定應(yīng)在運(yùn)行時(shí)如何處理、實(shí)例化和使用該組件。
關(guān)于@Component()
該如何使用可以參考,這里不多介紹。我們來(lái)看看這個(gè)裝飾器的定義:
// 提供 Angular 組件的配置元數(shù)據(jù)接口定義 // Angular 中,組件是指令的子集,始終與模板相關(guān)聯(lián) export interface Component extends Directive { // changeDetection 用于此組件的變更檢測(cè)策略 // 實(shí)例化組件時(shí),Angular 將創(chuàng)建一個(gè)更改檢測(cè)器,該更改檢測(cè)器負(fù)責(zé)傳播組件的綁定。 changeDetection?: ChangeDetectionStrategy; // 定義對(duì)其視圖 DOM 子對(duì)象可見(jiàn)的可注入對(duì)象的集合 viewProviders?: Provider[]; // 包含組件的模塊的模塊ID,該組件必須能夠解析模板和樣式的相對(duì) URL moduleId?: string; ... // 模板和 CSS 樣式的封裝策略 encapsulation?: ViewEncapsulation; // 覆蓋默認(rèn)的插值起始和終止定界符(`{{`和`}}`) interpolation?: [string, string]; } // 組件裝飾器和元數(shù)據(jù) export const Component: ComponentDecorator = makeDecorator( 'Component', // 使用默認(rèn)的 CheckAlways 策略,在該策略中,更改檢測(cè)是自動(dòng)進(jìn)行的,直到明確停用為止。 (c: Component = {}) => ({changeDetection: ChangeDetectionStrategy.Default, ...c}), Directive, undefined, (type: Type<any>, meta: Component) => SWITCH_COMPILE_COMPONENT(type, meta));
以上便是組件裝飾、組件元數(shù)據(jù)的定義,我們來(lái)看看裝飾器的創(chuàng)建過(guò)程。
裝飾器的創(chuàng)建過(guò)程
我們可以從源碼中找到,組件和指令的裝飾器都會(huì)通過(guò)makeDecorator()
來(lái)產(chǎn)生:
export function makeDecorator<T>( name: string, props?: (...args: any[]) => any, parentClass?: any, // 裝飾器名字和屬性 additionalProcessing?: (type: Type<T>) => void, typeFn?: (type: Type<T>, ...args: any[]) => void): {new (...args: any[]): any; (...args: any[]): any; (...args: any[]): (cls: any) => any;} { // noSideEffects 用于確認(rèn)閉包編譯器包裝的函數(shù)沒(méi)有副作用 return noSideEffects(() => { const metaCtor = makeMetadataCtor(props); // 裝飾器工廠 function DecoratorFactory( this: unknown|typeof DecoratorFactory, ...args: any[]): (cls: Type<T>) => any { if (this instanceof DecoratorFactory) { // 賦值元數(shù)據(jù) metaCtor.call(this, ...args); return this as typeof DecoratorFactory; } // 創(chuàng)建裝飾器工廠 const annotationInstance = new (DecoratorFactory as any)(...args); return function TypeDecorator(cls: Type<T>) { // 編譯類(lèi) if (typeFn) typeFn(cls, ...args); // 使用 Object.defineProperty 很重要,因?yàn)樗鼤?huì)創(chuàng)建不可枚舉的屬性,從而防止該屬性在子類(lèi)化過(guò)程中被復(fù)制。 const annotations = cls.hasOwnProperty(ANNOTATIONS) ? (cls as any)[ANNOTATIONS] : Object.defineProperty(cls, ANNOTATIONS, {value: []})[ANNOTATIONS]; annotations.push(annotationInstance); // 特定邏輯的執(zhí)行 if (additionalProcessing) additionalProcessing(cls); return cls; }; } if (parentClass) { // 繼承父類(lèi) DecoratorFactory.prototype = Object.create(parentClass.prototype); } DecoratorFactory.prototype.ngMetadataName = name; (DecoratorFactory as any).annotationCls = DecoratorFactory; return DecoratorFactory as any; }); }
在上面的例子中,我們通過(guò)makeDecorator()
產(chǎn)生了一個(gè)用于定義組件的Component
裝飾器工廠。當(dāng)使用@Component()
創(chuàng)建組件時(shí),Angular 會(huì)根據(jù)元數(shù)據(jù)來(lái)編譯組件。
根據(jù)裝飾器元數(shù)據(jù)編譯組件
Angular 會(huì)根據(jù)該裝飾器元數(shù)據(jù),來(lái)編譯 Angular 組件,然后將生成的組件定義(?cmp
@Component()
umfassen die Funktionen dieses Dekorators: ????????Markieren Sie die Klasse als Winkelkomponenten. ????????Stellt konfigurierbare Metadaten bereit, die bestimmen, wie die Komponente zur Laufzeit verarbeitet, instanziiert und verwendet werden soll. ????????Informationen zur Verwendung von @Component()
finden Sie unter ??, hier gibt es nicht viel Einführung. Werfen wir einen Blick auf die Definition dieses Dekorators: ??export function compileComponent(type: Type<any>, metadata: Component): void { // 初始化 ngDevMode (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode(); let ngComponentDef: any = null; // 元數(shù)據(jù)可能具有需要解析的資源 maybeQueueResolutionOfComponentResources(type, metadata); // 這里使用的功能與指令相同,因?yàn)檫@只是創(chuàng)建 ngFactoryDef 所需的元數(shù)據(jù)的子集 addDirectiveFactoryDef(type, metadata); Object.defineProperty(type, NG_COMP_DEF, { get: () => { if (ngComponentDef === null) { const compiler = getCompilerFacade(); // 根據(jù)元數(shù)據(jù)解析組件 if (componentNeedsResolution(metadata)) { ... // 異常處理 } ... // 創(chuàng)建編譯組件需要的完整元數(shù)據(jù) const templateUrl = metadata.templateUrl || `ng:///${type.name}/template.html`; const meta: R3ComponentMetadataFacade = { ...directiveMetadata(type, metadata), typeSourceSpan: compiler.createParseSourceSpan('Component', type.name, templateUrl), template: metadata.template || '', preserveWhitespaces, styles: metadata.styles || EMPTY_ARRAY, animations: metadata.animations, directives: [], changeDetection: metadata.changeDetection, pipes: new Map(), encapsulation, interpolation: metadata.interpolation, viewProviders: metadata.viewProviders || null, }; // 編譯過(guò)程需要計(jì)算深度,以便確認(rèn)編譯是否最終完成 compilationDepth++; try { if (meta.usesInheritance) { addDirectiveDefToUndecoratedParents(type); } // 根據(jù)模板、環(huán)境和組件需要的元數(shù)據(jù),來(lái)編譯組件 ngComponentDef = compiler.compileComponent(angularCoreEnv, templateUrl, meta); } finally { // 即使編譯失敗,也請(qǐng)確保減少編譯深度 compilationDepth--; } if (compilationDepth === 0) { // 當(dāng)執(zhí)行 NgModule 裝飾器時(shí),我們將模塊定義加入隊(duì)列,以便僅在所有聲明都已解析的情況下才將隊(duì)列出隊(duì),并將其自身作為模塊作用域添加到其所有聲明中 // 此調(diào)用運(yùn)行檢查以查看隊(duì)列中的任何模塊是否可以出隊(duì),并將范圍添加到它們的聲明中 flushModuleScopingQueueAsMuchAsPossible(); } // 如果組件編譯是異步的,則聲明該組件的 @NgModule 批注可以執(zhí)行并在組件類(lèi)型上設(shè)置 ngSelectorScope 屬性 // 這允許組件在完成編譯后,使用模塊中的 directiveDefs 對(duì)其自身進(jìn)行修補(bǔ) if (hasSelectorScope(type)) { const scopes = transitiveScopesFor(type.ngSelectorScope); patchComponentDefWithScope(ngComponentDef, scopes); } } return ngComponentDef; }, ... }); }??Das Obige ist die Definition der Komponentendekoration und der Komponentenmetadaten. Werfen wir einen Blick auf den Erstellungsprozess des Dekorators. ????????Der Erstellungsprozess von Dekoratoren????????Wir k?nnen ihn aus dem Quellcode finden. Dekoratoren für Komponenten und Anweisungen werden über
makeDecorator()
generiert: ??export class CompileMetadataResolver { private _nonNormalizedDirectiveCache = new Map<Type, {annotation: Directive, metadata: cpl.CompileDirectiveMetadata}>(); // 使用 Map 的方式來(lái)保存 private _directiveCache = new Map<Type, cpl.CompileDirectiveMetadata>(); private _summaryCache = new Map<Type, cpl.CompileTypeSummary|null>(); private _pipeCache = new Map<Type, cpl.CompilePipeMetadata>(); private _ngModuleCache = new Map<Type, cpl.CompileNgModuleMetadata>(); private _ngModuleOfTypes = new Map<Type, Type>(); private _shallowModuleCache = new Map<Type, cpl.CompileShallowModuleMetadata>(); constructor( private _config: CompilerConfig, private _htmlParser: HtmlParser, private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver<any>, private _schemaRegistry: ElementSchemaRegistry, private _directiveNormalizer: DirectiveNormalizer, private _console: Console, private _staticSymbolCache: StaticSymbolCache, private _reflector: CompileReflector, private _errorCollector?: ErrorCollector) {} // 清除特定某個(gè)指令的元數(shù)據(jù) clearCacheFor(type: Type) { const dirMeta = this._directiveCache.get(type); this._directiveCache.delete(type); ... } // 清除所有元數(shù)據(jù) clearCache(): void { this._directiveCache.clear(); ... } /** * 加載 NgModule 中,已聲明的指令和的管道 */ loadNgModuleDirectiveAndPipeMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true): Promise<any> { const ngModule = this.getNgModuleMetadata(moduleType, throwIfNotFound); const loading: Promise<any>[] = []; if (ngModule) { ngModule.declaredDirectives.forEach((id) => { const promise = this.loadDirectiveMetadata(moduleType, id.reference, isSync); if (promise) { loading.push(promise); } }); ngModule.declaredPipes.forEach((id) => this._loadPipeMetadata(id.reference)); } return Promise.all(loading); } // 加載指令(組件)元數(shù)據(jù) loadDirectiveMetadata(ngModuleType: any, directiveType: any, isSync: boolean): SyncAsync<null> { // 若已加載,則直接返回 if (this._directiveCache.has(directiveType)) { return null; } directiveType = resolveForwardRef(directiveType); const {annotation, metadata} = this.getNonNormalizedDirectiveMetadata(directiveType)!; // 創(chuàng)建指令(組件)元數(shù)據(jù) const createDirectiveMetadata = (templateMetadata: cpl.CompileTemplateMetadata|null) => { const normalizedDirMeta = new cpl.CompileDirectiveMetadata({ isHost: false, type: metadata.type, isComponent: metadata.isComponent, selector: metadata.selector, exportAs: metadata.exportAs, changeDetection: metadata.changeDetection, inputs: metadata.inputs, outputs: metadata.outputs, hostListeners: metadata.hostListeners, hostProperties: metadata.hostProperties, hostAttributes: metadata.hostAttributes, providers: metadata.providers, viewProviders: metadata.viewProviders, queries: metadata.queries, guards: metadata.guards, viewQueries: metadata.viewQueries, entryComponents: metadata.entryComponents, componentViewType: metadata.componentViewType, rendererType: metadata.rendererType, componentFactory: metadata.componentFactory, template: templateMetadata }); if (templateMetadata) { this.initComponentFactory(metadata.componentFactory!, templateMetadata.ngContentSelectors); } // 存儲(chǔ)完整的元數(shù)據(jù)信息,以及元數(shù)據(jù)摘要信息 this._directiveCache.set(directiveType, normalizedDirMeta); this._summaryCache.set(directiveType, normalizedDirMeta.toSummary()); return null; }; if (metadata.isComponent) { // 如果是組件,該過(guò)程可能為異步過(guò)程,則需要等待異步過(guò)程結(jié)束后的模板返回 const template = metadata.template !; const templateMeta = this._directiveNormalizer.normalizeTemplate({ ngModuleType, componentType: directiveType, moduleUrl: this._reflector.componentModuleUrl(directiveType, annotation), encapsulation: template.encapsulation, template: template.template, templateUrl: template.templateUrl, styles: template.styles, styleUrls: template.styleUrls, animations: template.animations, interpolation: template.interpolation, preserveWhitespaces: template.preserveWhitespaces }); if (isPromise(templateMeta) && isSync) { this._reportError(componentStillLoadingError(directiveType), directiveType); return null; } // 并將元數(shù)據(jù)進(jìn)行存儲(chǔ) return SyncAsync.then(templateMeta, createDirectiveMetadata); } else { // 指令,直接存儲(chǔ)元數(shù)據(jù) createDirectiveMetadata(null); return null; } } // 獲取給定指令(組件)的元數(shù)據(jù)信息 getDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata { const dirMeta = this._directiveCache.get(directiveType)!; ... return dirMeta; } // 獲取給定指令(組件)的元數(shù)據(jù)摘要信息 getDirectiveSummary(dirType: any): cpl.CompileDirectiveSummary { const dirSummary = <cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive); ... return dirSummary; } }??Im obigen Beispiel: Wir verwenden
makeDecorator()
, um eine Component
-Decorator-Factory zum Definieren von Komponenten zu generieren. Wenn Sie eine Komponente mit @Component()
erstellen, kompiliert Angular die Komponente basierend auf den Metadaten. ????????Komponenten basierend auf Decorator-Metadaten kompilieren????????Angular kompiliert Angular-Komponenten basierend auf den Decorator-Metadaten und patcht dann die generierte Komponentendefinition (?cmp
) auf den Komponententyp:??export function compileComponent(type: Type<any>, metadata: Component): void { // 初始化 ngDevMode (typeof ngDevMode === 'undefined' || ngDevMode) && initNgDevMode(); let ngComponentDef: any = null; // 元數(shù)據(jù)可能具有需要解析的資源 maybeQueueResolutionOfComponentResources(type, metadata); // 這里使用的功能與指令相同,因?yàn)檫@只是創(chuàng)建 ngFactoryDef 所需的元數(shù)據(jù)的子集 addDirectiveFactoryDef(type, metadata); Object.defineProperty(type, NG_COMP_DEF, { get: () => { if (ngComponentDef === null) { const compiler = getCompilerFacade(); // 根據(jù)元數(shù)據(jù)解析組件 if (componentNeedsResolution(metadata)) { ... // 異常處理 } ... // 創(chuàng)建編譯組件需要的完整元數(shù)據(jù) const templateUrl = metadata.templateUrl || `ng:///${type.name}/template.html`; const meta: R3ComponentMetadataFacade = { ...directiveMetadata(type, metadata), typeSourceSpan: compiler.createParseSourceSpan('Component', type.name, templateUrl), template: metadata.template || '', preserveWhitespaces, styles: metadata.styles || EMPTY_ARRAY, animations: metadata.animations, directives: [], changeDetection: metadata.changeDetection, pipes: new Map(), encapsulation, interpolation: metadata.interpolation, viewProviders: metadata.viewProviders || null, }; // 編譯過(guò)程需要計(jì)算深度,以便確認(rèn)編譯是否最終完成 compilationDepth++; try { if (meta.usesInheritance) { addDirectiveDefToUndecoratedParents(type); } // 根據(jù)模板、環(huán)境和組件需要的元數(shù)據(jù),來(lái)編譯組件 ngComponentDef = compiler.compileComponent(angularCoreEnv, templateUrl, meta); } finally { // 即使編譯失敗,也請(qǐng)確保減少編譯深度 compilationDepth--; } if (compilationDepth === 0) { // 當(dāng)執(zhí)行 NgModule 裝飾器時(shí),我們將模塊定義加入隊(duì)列,以便僅在所有聲明都已解析的情況下才將隊(duì)列出隊(duì),并將其自身作為模塊作用域添加到其所有聲明中 // 此調(diào)用運(yùn)行檢查以查看隊(duì)列中的任何模塊是否可以出隊(duì),并將范圍添加到它們的聲明中 flushModuleScopingQueueAsMuchAsPossible(); } // 如果組件編譯是異步的,則聲明該組件的 @NgModule 批注可以執(zhí)行并在組件類(lèi)型上設(shè)置 ngSelectorScope 屬性 // 這允許組件在完成編譯后,使用模塊中的 directiveDefs 對(duì)其自身進(jìn)行修補(bǔ) if (hasSelectorScope(type)) { const scopes = transitiveScopesFor(type.ngSelectorScope); patchComponentDefWithScope(ngComponentDef, scopes); } } return ngComponentDef; }, ... }); }
編譯組件的過(guò)程可能是異步的(比如需要解析組件模板或其他資源的 URL)。如果編譯不是立即進(jìn)行的,compileComponent
會(huì)將資源解析加入到全局隊(duì)列中,并且將無(wú)法返回?cmp
,直到通過(guò)調(diào)用resolveComponentResources
解決了全局隊(duì)列為止。
編譯過(guò)程中的元數(shù)據(jù)
元數(shù)據(jù)是有關(guān)類(lèi)的信息,但它不是類(lèi)的屬性。因此,用于配置類(lèi)的定義和行為的這些數(shù)據(jù),不應(yīng)該存儲(chǔ)在該類(lèi)的實(shí)例中,我們還需要在其他地方保存此數(shù)據(jù)。
在 Angular 中,編譯過(guò)程產(chǎn)生的元數(shù)據(jù),會(huì)使用CompileMetadataResolver
來(lái)進(jìn)行管理和維護(hù),這里我們主要看指令(組件)相關(guān)的邏輯:
export class CompileMetadataResolver { private _nonNormalizedDirectiveCache = new Map<Type, {annotation: Directive, metadata: cpl.CompileDirectiveMetadata}>(); // 使用 Map 的方式來(lái)保存 private _directiveCache = new Map<Type, cpl.CompileDirectiveMetadata>(); private _summaryCache = new Map<Type, cpl.CompileTypeSummary|null>(); private _pipeCache = new Map<Type, cpl.CompilePipeMetadata>(); private _ngModuleCache = new Map<Type, cpl.CompileNgModuleMetadata>(); private _ngModuleOfTypes = new Map<Type, Type>(); private _shallowModuleCache = new Map<Type, cpl.CompileShallowModuleMetadata>(); constructor( private _config: CompilerConfig, private _htmlParser: HtmlParser, private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver<any>, private _schemaRegistry: ElementSchemaRegistry, private _directiveNormalizer: DirectiveNormalizer, private _console: Console, private _staticSymbolCache: StaticSymbolCache, private _reflector: CompileReflector, private _errorCollector?: ErrorCollector) {} // 清除特定某個(gè)指令的元數(shù)據(jù) clearCacheFor(type: Type) { const dirMeta = this._directiveCache.get(type); this._directiveCache.delete(type); ... } // 清除所有元數(shù)據(jù) clearCache(): void { this._directiveCache.clear(); ... } /** * 加載 NgModule 中,已聲明的指令和的管道 */ loadNgModuleDirectiveAndPipeMetadata(moduleType: any, isSync: boolean, throwIfNotFound = true): Promise<any> { const ngModule = this.getNgModuleMetadata(moduleType, throwIfNotFound); const loading: Promise<any>[] = []; if (ngModule) { ngModule.declaredDirectives.forEach((id) => { const promise = this.loadDirectiveMetadata(moduleType, id.reference, isSync); if (promise) { loading.push(promise); } }); ngModule.declaredPipes.forEach((id) => this._loadPipeMetadata(id.reference)); } return Promise.all(loading); } // 加載指令(組件)元數(shù)據(jù) loadDirectiveMetadata(ngModuleType: any, directiveType: any, isSync: boolean): SyncAsync<null> { // 若已加載,則直接返回 if (this._directiveCache.has(directiveType)) { return null; } directiveType = resolveForwardRef(directiveType); const {annotation, metadata} = this.getNonNormalizedDirectiveMetadata(directiveType)!; // 創(chuàng)建指令(組件)元數(shù)據(jù) const createDirectiveMetadata = (templateMetadata: cpl.CompileTemplateMetadata|null) => { const normalizedDirMeta = new cpl.CompileDirectiveMetadata({ isHost: false, type: metadata.type, isComponent: metadata.isComponent, selector: metadata.selector, exportAs: metadata.exportAs, changeDetection: metadata.changeDetection, inputs: metadata.inputs, outputs: metadata.outputs, hostListeners: metadata.hostListeners, hostProperties: metadata.hostProperties, hostAttributes: metadata.hostAttributes, providers: metadata.providers, viewProviders: metadata.viewProviders, queries: metadata.queries, guards: metadata.guards, viewQueries: metadata.viewQueries, entryComponents: metadata.entryComponents, componentViewType: metadata.componentViewType, rendererType: metadata.rendererType, componentFactory: metadata.componentFactory, template: templateMetadata }); if (templateMetadata) { this.initComponentFactory(metadata.componentFactory!, templateMetadata.ngContentSelectors); } // 存儲(chǔ)完整的元數(shù)據(jù)信息,以及元數(shù)據(jù)摘要信息 this._directiveCache.set(directiveType, normalizedDirMeta); this._summaryCache.set(directiveType, normalizedDirMeta.toSummary()); return null; }; if (metadata.isComponent) { // 如果是組件,該過(guò)程可能為異步過(guò)程,則需要等待異步過(guò)程結(jié)束后的模板返回 const template = metadata.template !; const templateMeta = this._directiveNormalizer.normalizeTemplate({ ngModuleType, componentType: directiveType, moduleUrl: this._reflector.componentModuleUrl(directiveType, annotation), encapsulation: template.encapsulation, template: template.template, templateUrl: template.templateUrl, styles: template.styles, styleUrls: template.styleUrls, animations: template.animations, interpolation: template.interpolation, preserveWhitespaces: template.preserveWhitespaces }); if (isPromise(templateMeta) && isSync) { this._reportError(componentStillLoadingError(directiveType), directiveType); return null; } // 并將元數(shù)據(jù)進(jìn)行存儲(chǔ) return SyncAsync.then(templateMeta, createDirectiveMetadata); } else { // 指令,直接存儲(chǔ)元數(shù)據(jù) createDirectiveMetadata(null); return null; } } // 獲取給定指令(組件)的元數(shù)據(jù)信息 getDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata { const dirMeta = this._directiveCache.get(directiveType)!; ... return dirMeta; } // 獲取給定指令(組件)的元數(shù)據(jù)摘要信息 getDirectiveSummary(dirType: any): cpl.CompileDirectiveSummary { const dirSummary = <cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive); ... return dirSummary; } }
可以看到,在編譯過(guò)程中,不管是組件、指令、管道,還是模塊,這些類(lèi)在編譯過(guò)程中的元數(shù)據(jù),都使用Map
來(lái)存儲(chǔ)。
總結(jié)
本節(jié)我們介紹了 Angular 中的裝飾器和元數(shù)據(jù),其中元數(shù)據(jù)用于描述類(lèi)的定義和行為。
在 Angular 編譯過(guò)程中,會(huì)使用Map
的數(shù)據(jù)結(jié)構(gòu)來(lái)維護(hù)和存儲(chǔ)裝飾器的元數(shù)據(jù),并根據(jù)這些元數(shù)據(jù)信息來(lái)編譯組件、指令、管道和模塊等。
更多編程相關(guān)知識(shí),請(qǐng)?jiān)L問(wèn):編程教學(xué)??!
Das obige ist der detaillierte Inhalt vonLassen Sie uns über Metadaten und Dekoratoren in Angular sprechen. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Hei?e KI -Werkzeuge

Undress AI Tool
Ausziehbilder kostenlos

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem v?llig kostenlosen KI-Gesichtstausch-Tool aus!

Hei?er Artikel

Hei?e Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Einleitung Letzte Woche habe ich eine Einleitung über das Scrapen von Webseiten zum Sammeln von Metadaten geschrieben und erw?hnt, dass es unm?glich sei, die Website der New York Times zu scrapen. Die Paywall der New York Times blockiert Ihre Versuche, grundlegende Metadaten zu sammeln. Aber es gibt eine M?glichkeit, dieses Problem mithilfe der New York Times API zu l?sen. Vor kurzem habe ich mit dem Aufbau einer Community-Website auf der Yii-Plattform begonnen, die ich in einem zukünftigen Tutorial ver?ffentlichen werde. Ich m?chte in der Lage sein, problemlos Links hinzuzufügen, die für den Inhalt der Website relevant sind. W?hrend Benutzer URLs problemlos in Formulare einfügen k?nnen, ist die Bereitstellung von Titel- und Quelleninformationen zeitaufw?ndig. Deshalb werde ich im heutigen Tutorial den Scraping-Code, den ich kürzlich geschrieben habe, erweitern, um die New York Times-API zum Sammeln von Schlagzeilen zu nutzen, wenn ich einen Link zur New York Times hinzufüge. Denken Sie daran, ich bin involviert

Angular.js ist eine frei zug?ngliche JavaScript-Plattform zum Erstellen dynamischer Anwendungen. Es erm?glicht Ihnen, verschiedene Aspekte Ihrer Anwendung schnell und klar auszudrücken, indem Sie die Syntax von HTML als Vorlagensprache erweitern. Angular.js bietet eine Reihe von Tools, die Sie beim Schreiben, Aktualisieren und Testen Ihres Codes unterstützen. Darüber hinaus bietet es viele Funktionen wie Routing und Formularverwaltung. In dieser Anleitung wird erl?utert, wie Angular auf Ubuntu24 installiert wird. Zuerst müssen Sie Node.js installieren. Node.js ist eine JavaScript-Ausführungsumgebung, die auf der ChromeV8-Engine basiert und es Ihnen erm?glicht, JavaScript-Code auf der Serverseite auszuführen. In Ub sein

Wie verwende ich den Monaco-Editor in Angular? Der folgende Artikel dokumentiert die Verwendung des Monaco-Editors in Angular, der kürzlich in einem Unternehmen verwendet wurde. Ich hoffe, dass er für alle hilfreich sein wird!

Mit der rasanten Entwicklung des Internets wird auch die Front-End-Entwicklungstechnologie st?ndig verbessert und iteriert. PHP und Angular sind zwei Technologien, die in der Frontend-Entwicklung weit verbreitet sind. PHP ist eine serverseitige Skriptsprache, die Aufgaben wie die Verarbeitung von Formularen, die Generierung dynamischer Seiten und die Verwaltung von Zugriffsberechtigungen übernehmen kann. Angular ist ein JavaScript-Framework, mit dem Single-Page-Anwendungen entwickelt und komponentenbasierte Webanwendungen erstellt werden k?nnen. In diesem Artikel erfahren Sie, wie Sie PHP und Angular für die Frontend-Entwicklung verwenden und wie Sie diese kombinieren

Kennen Sie Angular Universal? Es kann dazu beitragen, dass die Website eine bessere SEO-Unterstützung bietet!

Mit Mutagen und dem eyeD3-Modul in Python k?nnen wir auf die Metadaten von Audiodateien zugreifen. Für Videometadaten k?nnen wir Filme und die OpenCV-Bibliothek in Python verwenden. Metadaten sind Daten, die Informationen über andere Daten bereitstellen, beispielsweise Audio- und Videodaten. Zu den Metadaten für Audio- und Videodateien geh?ren Dateiformat, Dateiaufl?sung, Dateigr??e, Dauer, Bitrate usw. Durch den Zugriff auf diese Metadaten k?nnen wir Medien effizienter verwalten und die Metadaten analysieren, um nützliche Informationen zu erhalten. In diesem Artikel werfen wir einen Blick auf einige der von Python bereitgestellten Bibliotheken oder Module für den Zugriff auf Metadaten von Audio- und Videodateien. Auf Audio-Metadaten zugreifen Einige Bibliotheken für den Zugriff auf Audiodatei-Metadaten nutzen Mutagenese

Die Authentifizierung ist einer der wichtigsten Teile jeder Webanwendung. In diesem Tutorial werden tokenbasierte Authentifizierungssysteme und ihre Unterschiede zu herk?mmlichen Anmeldesystemen erl?utert. Am Ende dieses Tutorials sehen Sie eine voll funktionsf?hige Demo, die in Angular und Node.js geschrieben wurde. Traditionelle Authentifizierungssysteme Bevor wir zu tokenbasierten Authentifizierungssystemen übergehen, werfen wir einen Blick auf traditionelle Authentifizierungssysteme. Der Benutzer gibt seinen Benutzernamen und sein Passwort im Anmeldeformular ein und klickt auf ?Anmelden“. Nachdem Sie die Anfrage gestellt haben, authentifizieren Sie den Benutzer im Backend, indem Sie die Datenbank abfragen. Wenn die Anfrage gültig ist, wird eine Sitzung mit den aus der Datenbank erhaltenen Benutzerinformationen erstellt und die Sitzungsinformationen werden im Antwortheader zurückgegeben, sodass die Sitzungs-ID im Browser gespeichert wird. Bietet Zugriff auf Anwendungen, die unterliegen

Das Standardanzeigeverhalten für Komponenten im Angular-Framework gilt nicht für Elemente auf Blockebene. Diese Designwahl f?rdert die Kapselung von Komponentenstilen und ermutigt Entwickler, bewusst zu definieren, wie jede Komponente angezeigt wird. Durch explizites Festlegen der CSS-Eigenschaft display kann die Anzeige von Angular-Komponenten vollst?ndig gesteuert werden, um das gewünschte Layout und die gewünschte Reaktionsf?higkeit zu erreichen.
