真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Angular中的元數(shù)據(jù)和裝飾器怎么應(yīng)用

本篇內(nèi)容主要講解“Angular中的元數(shù)據(jù)和裝飾器怎么應(yīng)用”,感興趣的朋友不妨來(lái)看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來(lái)帶大家學(xué)習(xí)“Angular中的元數(shù)據(jù)和裝飾器怎么應(yīng)用”吧!

專(zhuān)注于為中小企業(yè)提供成都做網(wǎng)站、網(wǎng)站制作服務(wù),電腦端+手機(jī)端+微信端的三站合一,更高效的管理,為中小企業(yè)威信免費(fèi)做網(wǎng)站提供優(yōu)質(zhì)的服務(wù)。我們立足成都,凝聚了一批互聯(lián)網(wǎng)行業(yè)人才,有力地推動(dòng)了成百上千家企業(yè)的穩(wěn)健成長(zhǎng),幫助中小企業(yè)通過(guò)網(wǎng)站建設(shè)實(shí)現(xiàn)規(guī)模擴(kuò)充和轉(zhuǎn)變。

Angular中的元數(shù)據(jù)和裝飾器怎么應(yīng)用

作為“為大型前端項(xiàng)目”而設(shè)計(jì)的前端框架,Angular 其實(shí)有許多值得參考和學(xué)習(xí)的設(shè)計(jì),本系列主要用于研究這些設(shè)計(jì)和功能的實(shí)現(xiàn)原理。本文主要圍繞 Angular 中隨處可見(jiàn)的元數(shù)據(jù),來(lái)進(jìn)行介紹。

裝飾器是使用 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ù)是描述用戶(hù)數(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ò)值名稱(chēng)來(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, 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(
    name: string, props?: (...args: any[]) => any, parentClass?: any, // 裝飾器名字和屬性
    additionalProcessing?: (type: Type) => void,
    typeFn?: (type: Type, ...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) => 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) {
        // 編譯類(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, 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();
  // 使用 Map 的方式來(lái)保存
  private _directiveCache = new Map(); 
  private _summaryCache = new Map();
  private _pipeCache = new Map();
  private _ngModuleCache = new Map();
  private _ngModuleOfTypes = new Map();
  private _shallowModuleCache = new Map();

  constructor(
      private _config: CompilerConfig, private _htmlParser: HtmlParser,
      private _ngModuleResolver: NgModuleResolver, private _directiveResolver: DirectiveResolver,
      private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver,
      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 {
    const ngModule = this.getNgModuleMetadata(moduleType, throwIfNotFound);
    const loading: Promise[] = [];
    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 {
    // 若已加載,則直接返回
    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 =
        this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
    ...
    return dirSummary;
  }
}

可以看到,在編譯過(guò)程中,不管是組件、指令、管道,還是模塊,這些類(lèi)在編譯過(guò)程中的元數(shù)據(jù),都使用Map來(lái)存儲(chǔ)。

到此,相信大家對(duì)“Angular中的元數(shù)據(jù)和裝飾器怎么應(yīng)用”有了更深的了解,不妨來(lái)實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢(xún),關(guān)注我們,繼續(xù)學(xué)習(xí)!


本文標(biāo)題:Angular中的元數(shù)據(jù)和裝飾器怎么應(yīng)用
本文來(lái)源:http://weahome.cn/article/jsodci.html

其他資訊

在線咨詢(xún)

微信咨詢(xún)

電話(huà)咨詢(xún)

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部