本篇文章繼續(xù)Angular的學(xué)習(xí),帶大家了解一下Angular中的元數(shù)據(jù)和裝飾器,簡(jiǎn)單了解一下他們的用法,希望對(duì)大家有所幫助!
作為“為大型前端項(xiàng)目”而設(shè)計(jì)的前端框架,Angular 其實(shí)有許多值得參考和學(xué)習(xí)的設(shè)計(jì),本系列主要用于研究這些設(shè)計(jì)和功能的實(shí)現(xiàn)原理。本文主要圍繞 Angular 中隨處可見(jiàn)的元數(shù)據(jù),來(lái)進(jìn)行介紹。【相關(guān)教程推薦:《angular教程》】
裝飾器是使用 Angular 進(jìn)行開(kāi)發(fā)時(shí)的核心概念。在 Angular 中,裝飾器用于為類(lèi)或?qū)傩愿郊釉獢?shù)據(jù),來(lái)讓自己知道那些類(lèi)或?qū)傩缘暮x,以及該如何處理它們。
裝飾器與元數(shù)據(jù)
不管是裝飾器還是元數(shù)據(jù),都不是由 Angular 提出的概念。因此,我們先來(lái)簡(jiǎn)單了解一下。
元數(shù)據(jù)(Metadata)
在通用的概念中,元數(shù)據(jù)是描述用戶數(shù)據(jù)的數(shù)據(jù)。它總結(jié)了有關(guān)數(shù)據(jù)的基本信息,可以使查找和使用特定數(shù)據(jù)實(shí)例更加容易。例如,作者,創(chuàng)建日期,修改日期和文件大小是非常基本的文檔元數(shù)據(jù)的示例。
在用于類(lèi)的場(chǎng)景下,元數(shù)據(jù)用于裝飾類(lèi),來(lái)描述類(lèi)的定義和行為,以便可以配置類(lèi)的預(yù)期行為。
裝飾器(Decorator)
裝飾器是 JavaScript 的一種語(yǔ)言特性,是一項(xiàng)位于階段 2(stage 2)的試驗(yàn)特性。
裝飾器是定義期間在類(lèi),類(lèi)元素或其他 JavaScript 語(yǔ)法形式上調(diào)用的函數(shù)。
裝飾器具有三個(gè)主要功能:
可以用具有相同語(yǔ)義的匹配值替換正在修飾的值。(例如,裝飾器可以將方法替換為另一種方法,將一個(gè)字段替換為另一個(gè)字段,將一個(gè)類(lèi)替換為另一個(gè)類(lèi),等等)。
可以將元數(shù)據(jù)與正在修飾的值相關(guān)聯(lián);可以從外部讀取此元數(shù)據(jù),并將其用于元編程和自我檢查。
可以通過(guò)元數(shù)據(jù)提供對(duì)正在修飾的值的訪問(wèn)。對(duì)于公共值,他們可以通過(guò)值名稱來(lái)實(shí)現(xiàn);對(duì)于私有值,它們接收訪問(wèn)器函數(shù),然后可以選擇共享它們。
本質(zhì)上,裝飾器可用于對(duì)值進(jìn)行元編程和向其添加功能,而無(wú)需從根本上改變其外部行為。
更多的內(nèi)容,可以參考 tc39/proposal-decorators 提案。
Angular 中的裝飾器和元數(shù)據(jù)
我們?cè)陂_(kāi)發(fā) Angular 應(yīng)用時(shí),不管是組件、指令,還是服務(wù)、模塊等,都需要通過(guò)裝飾器來(lái)進(jìn)行定義和開(kāi)發(fā)。裝飾器會(huì)出現(xiàn)在類(lèi)定義的緊前方,用來(lái)聲明該類(lèi)具有指定的類(lèi)型,并且提供適合該類(lèi)型的元數(shù)據(jù)。
比如,我們可以用下列裝飾器來(lái)聲明 Angular 的類(lèi):@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
)修補(bǔ)到組件類(lèi)型上:
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é)??!
以上是聊聊Angular中的元數(shù)據(jù)(Metadata)和裝飾器(Decorator)的詳細(xì)內(nèi)容。更多信息請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

熱AI工具

Undress AI Tool
免費(fèi)脫衣服圖片

Undresser.AI Undress
人工智能驅(qū)動(dòng)的應(yīng)用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover
用于從照片中去除衣服的在線人工智能工具。

Clothoff.io
AI脫衣機(jī)

Video Face Swap
使用我們完全免費(fèi)的人工智能換臉工具輕松在任何視頻中換臉!

熱門(mén)文章

熱工具

記事本++7.3.1
好用且免費(fèi)的代碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
功能強(qiáng)大的PHP集成開(kāi)發(fā)環(huán)境

Dreamweaver CS6
視覺(jué)化網(wǎng)頁(yè)開(kāi)發(fā)工具

SublimeText3 Mac版
神級(jí)代碼編輯軟件(SublimeText3)

簡(jiǎn)介上周,我寫(xiě)了一篇關(guān)于抓取網(wǎng)頁(yè)以收集元數(shù)據(jù)的介紹,并提到不可能抓取《紐約時(shí)報(bào)》網(wǎng)站?!都~約時(shí)報(bào)》付費(fèi)墻會(huì)阻止您收集基本元數(shù)據(jù)的嘗試。但有一種方法可以使用紐約時(shí)報(bào)API來(lái)解決這個(gè)問(wèn)題。最近我開(kāi)始在Yii平臺(tái)上構(gòu)建一個(gè)社區(qū)網(wǎng)站,我將在以后的教程中發(fā)布該網(wǎng)站。我希望能夠輕松添加與網(wǎng)站內(nèi)容相關(guān)的鏈接。雖然人們可以輕松地將URL粘貼到表單中,但提供標(biāo)題和來(lái)源信息卻非常耗時(shí)。因此,在今天的教程中,我將擴(kuò)展我最近編寫(xiě)的抓取代碼,以在添加《紐約時(shí)報(bào)》鏈接時(shí)利用《紐約時(shí)報(bào)》API來(lái)收集頭條新聞。請(qǐng)記住,我參與了

Angular.js是一種可自由訪問(wèn)的JavaScript平臺(tái),用于創(chuàng)建動(dòng)態(tài)應(yīng)用程序。它允許您通過(guò)擴(kuò)展HTML的語(yǔ)法作為模板語(yǔ)言,以快速、清晰地表示應(yīng)用程序的各個(gè)方面。Angular.js提供了一系列工具,可幫助您編寫(xiě)、更新和測(cè)試代碼。此外,它還提供了許多功能,如路由和表單管理。本指南將討論在Ubuntu24上安裝Angular的方法。首先,您需要安裝Node.js。Node.js是一個(gè)基于ChromeV8引擎的JavaScript運(yùn)行環(huán)境,可讓您在服務(wù)器端運(yùn)行JavaScript代碼。要在Ub

angular中怎么使用monaco-editor?下面本篇文章記錄下最近的一次業(yè)務(wù)中用到的 monaco-editor 在 angular 中的使用,希望對(duì)大家有所幫助!

隨著互聯(lián)網(wǎng)的飛速發(fā)展,前端開(kāi)發(fā)技術(shù)也在不斷改進(jìn)和迭代。PHP和Angular是兩種廣泛應(yīng)用于前端開(kāi)發(fā)的技術(shù)。PHP是一種服務(wù)器端腳本語(yǔ)言,可以處理表單、生成動(dòng)態(tài)頁(yè)面和管理訪問(wèn)權(quán)限等任務(wù)。而Angular是一種JavaScript的框架,可以用于開(kāi)發(fā)單頁(yè)面應(yīng)用和構(gòu)建組件化的Web應(yīng)用程序。本篇文章將介紹如何使用PHP和Angular進(jìn)行前端開(kāi)發(fā),以及如何將它們

你知道 Angular Universal 嗎?可以幫助網(wǎng)站提供更好的 SEO 支持哦!

我們可以使用Mutagen和Python中的eyeD3模塊訪問(wèn)音頻文件的元數(shù)據(jù)。對(duì)于視頻元數(shù)據(jù),我們可以使用電影和Python中的OpenCV庫(kù)。元數(shù)據(jù)是提供有關(guān)其他數(shù)據(jù)(例如音頻和視頻數(shù)據(jù))的信息的數(shù)據(jù)。音頻和視頻文件的元數(shù)據(jù)包括文件格式、文件分辨率、文件大小、持續(xù)時(shí)間、比特率等。通過(guò)訪問(wèn)這些元數(shù)據(jù),我們可以更有效地管理媒體并分析元數(shù)據(jù)以獲得一些有用的信息。在本文中,我們將了解Python提供的一些用于訪問(wèn)音頻和視頻文件元數(shù)據(jù)的庫(kù)或模塊。訪問(wèn)音頻元數(shù)據(jù)一些用于訪問(wèn)音頻文件元數(shù)據(jù)的庫(kù)是-使用誘變

身份驗(yàn)證是任何Web應(yīng)用程序中最重要的部分之一。本教程討論基于令牌的身份驗(yàn)證系統(tǒng)以及它們與傳統(tǒng)登錄系統(tǒng)的區(qū)別。在本教程結(jié)束時(shí),您將看到一個(gè)用Angular和Node.js編寫(xiě)的完整工作演示。傳統(tǒng)身份驗(yàn)證系統(tǒng)在繼續(xù)基于令牌的身份驗(yàn)證系統(tǒng)之前,讓我們先看一下傳統(tǒng)的身份驗(yàn)證系統(tǒng)。用戶在登錄表單中提供用戶名和密碼,然后點(diǎn)擊登錄。發(fā)出請(qǐng)求后,通過(guò)查詢數(shù)據(jù)庫(kù)在后端驗(yàn)證用戶。如果請(qǐng)求有效,則使用從數(shù)據(jù)庫(kù)中獲取的用戶信息創(chuàng)建會(huì)話,然后在響應(yīng)頭中返回會(huì)話信息,以便將會(huì)話ID存儲(chǔ)在瀏覽器中。提供用于訪問(wèn)應(yīng)用程序中受

Angular框架中組件的默認(rèn)顯示行為不是塊級(jí)元素。這種設(shè)計(jì)選擇促進(jìn)了組件樣式的封裝,并鼓勵(lì)開(kāi)發(fā)人員有意識(shí)地定義每個(gè)組件的顯示方式。通過(guò)顯式設(shè)置CSS屬性 display,Angular組件的顯示可以完全控制,從而實(shí)現(xiàn)所需的布局和響應(yīng)能力。
