検索
ホームページウェブフロントエンドjsチュートリアルAngular のメタデータとデコレータについて話しましょう

この記事では、Angular の学習を継続し、Angular のメタデータとデコレータを理解し、それらの使用法を簡単に理解します。

Angular のメタデータとデコレータについて話しましょう

「大規模なフロントエンド プロジェクト向け」に設計されたフロントエンド フレームワークとして、Angular には実際に参考にして学ぶ価値のある設計が数多くあります。このシリーズは主に次の用途に使用されます。これらのデザインと機能を研究し、実現原理を研究します。この記事では、Angular の随所に存在するメタデータを主に紹介します。 [関連チュートリアルの推奨事項: "angular チュートリアル"]

デコレーターは、Angular を使用して開発する場合の中心的な概念です。 Angular では、デコレータを使用してクラスまたはプロパティにメタデータを添付し、それらのクラスまたはプロパティの意味とその処理方法を知らせます。

デコレータとメタデータ

デコレータもメタデータも、Angular によって提案された概念ではありません。それでは、まず簡単に見てみましょう。

メタデータ

一般的な概念では、メタデータはユーザー データを説明するデータです。データに関する基本情報が要約されており、特定のデータ インスタンスの検索と使用が容易になります。たとえば、作成者、作成日、変更日、ファイル サイズなどは、非常に基本的なドキュメント メタデータの例です。

クラスに使用されるシナリオでは、メタデータを使用してクラスを装飾し、クラスの定義と動作を記述し、クラスの期待される動作を構成できるようにします。

Decorator

Decorator は JavaScript の言語機能で、ステージ 2. テスト特性にあります。

デコレーターは、定義中にクラス、クラス要素、またはその他の JavaScript 構文フォームで呼び出される関数です。

デコレータには 3 つの主な機能があります。

  • デコレータの値を、同じセマンティクスを持つ一致する値に置き換えることができます。 (たとえば、デコレータはメソッドを別のメソッドに置き換えたり、あるフィールドを別のフィールドに置き換えたり、あるクラスを別のクラスに置き換えたりすることができます)。

  • メタデータは、変更される値に関連付けることができます。このメタデータは外部から読み取って、メタプログラミングや自己検査に使用できます。

  • 変更される値へのアクセスは、メタデータを通じて提供できます。パブリック値の場合は、値名を介してこれを行い、プライベート値の場合は、オプションで共有できるアクセサー関数を受け取ります。

本質的に、デコレータを使用すると、外部の動作を根本的に変更することなく、値をメタプログラムし、それに機能を追加できます。

詳細については、tc39/proposal-decorators 提案を参照してください。

Angular のデコレータとメタデータ

Angular アプリケーションを開発するときは、コンポーネント、命令、サービス、モジュールなど、すべてデコレータを通じて定義および開発する必要があります。デコレーターはクラス定義の直前に表示され、クラスが指定された型を持つことを宣言し、その型に適したメタデータを提供するために使用されます。

たとえば、次のデコレータを使用して Angular クラスを宣言できます: @Component()@Directive()@Pipe()@Injectable()@NgModule()

デコレータとメタデータを使用してクラスの動作を変更する

@Component() を例に挙げます。デコレータの機能は次のとおりです。

  • クラスを Angular コンポーネントとしてマークします。

  • コンポーネントを実行時にどのように処理、インスタンス化、使用するかを決定する構成可能なメタデータを提供します。

@Component() の使い方については を参照してください。ここでは紹介しません。このデコレータの定義を見てみましょう:

// 提供 Angular 组件的配置元数据接口定义
// Angular 中,组件是指令的子集,始终与模板相关联
export interface Component extends Directive {
  // changeDetection 用于此组件的变更检测策略
  // 实例化组件时,Angular 将创建一个更改检测器,该更改检测器负责传播组件的绑定。
  changeDetection?: ChangeDetectionStrategy;
  // 定义对其视图 DOM 子对象可见的可注入对象的集合
  viewProviders?: Provider[];
  // 包含组件的模块的模块ID,该组件必须能够解析模板和样式的相对 URL
  moduleId?: string;
  ...
  // 模板和 CSS 样式的封装策略
  encapsulation?: ViewEncapsulation;
  // 覆盖默认的插值起始和终止定界符(`{{`和`}}`)
  interpolation?: [string, string];
}

// 组件装饰器和元数据
export const Component: ComponentDecorator = makeDecorator(
    'Component',
    // 使用默认的 CheckAlways 策略,在该策略中,更改检测是自动进行的,直到明确停用为止。
    (c: Component = {}) => ({changeDetection: ChangeDetectionStrategy.Default, ...c}),
    Directive, undefined,
    (type: Type<any>, meta: Component) => SWITCH_COMPILE_COMPONENT(type, meta));

上記はコンポーネント デコレーションとコンポーネント メタデータの定義です。デコレータの作成プロセスを見てみましょう。

デコレータの作成プロセス

ソースコードから見つけることができますコンポーネントと命令のデコレータは を渡しますmakeDecorator() 生成するには:

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 用于确认闭包编译器包装的函数没有副作用
  return noSideEffects(() => { 
    const metaCtor = makeMetadataCtor(props);
    // 装饰器工厂
    function DecoratorFactory(
        this: unknown|typeof DecoratorFactory, ...args: any[]): (cls: Type<T>) => any {
      if (this instanceof DecoratorFactory) {
        // 赋值元数据
        metaCtor.call(this, ...args);
        return this as typeof DecoratorFactory;
      }
      // 创建装饰器工厂
      const annotationInstance = new (DecoratorFactory as any)(...args);
      return function TypeDecorator(cls: Type<T>) {
        // 编译类
        if (typeFn) typeFn(cls, ...args);
        // 使用 Object.defineProperty 很重要,因为它会创建不可枚举的属性,从而防止该属性在子类化过程中被复制。
        const annotations = cls.hasOwnProperty(ANNOTATIONS) ?
            (cls as any)[ANNOTATIONS] :
            Object.defineProperty(cls, ANNOTATIONS, {value: []})[ANNOTATIONS];
        annotations.push(annotationInstance);
        // 特定逻辑的执行
        if (additionalProcessing) additionalProcessing(cls);

        return cls;
      };
    }
    if (parentClass) {
      // 继承父类
      DecoratorFactory.prototype = Object.create(parentClass.prototype);
    }
    DecoratorFactory.prototype.ngMetadataName = name;
    (DecoratorFactory as any).annotationCls = DecoratorFactory;
    return DecoratorFactory as any;
  });
}

上記の例では、makeDecorator() を使用して、コンポーネントを定義するための Component デコレータ ファクトリを生成します。 @Component() を使用してコンポーネントを作成すると、Angular はメタデータに基づいてコンポーネントをコンパイルします。

デコレータ メタデータに基づいてコンポーネントをコンパイルする

Angular は、デコレータ メタデータに基づいて Angular コンポーネントをコンパイルし、生成されたコンポーネントを変換します定義 (ɵcmp) はコンポーネント タイプにパッチされます:

export function compileComponent(type: Type<any>, metadata: Component): void {
  // 初始化 ngDevMode
  (typeof ngDevMode === &#39;undefined&#39; || ngDevMode) && initNgDevMode();
  let ngComponentDef: any = null;
  // 元数据可能具有需要解析的资源
  maybeQueueResolutionOfComponentResources(type, metadata);
  // 这里使用的功能与指令相同,因为这只是创建 ngFactoryDef 所需的元数据的子集
  addDirectiveFactoryDef(type, metadata);
  Object.defineProperty(type, NG_COMP_DEF, {
    get: () => {
      if (ngComponentDef === null) {
        const compiler = getCompilerFacade();
        // 根据元数据解析组件
        if (componentNeedsResolution(metadata)) {
          ...
          // 异常处理
        }
        ...
        // 创建编译组件需要的完整元数据
        const templateUrl = metadata.templateUrl || `ng:///${type.name}/template.html`;
        const meta: R3ComponentMetadataFacade = {
          ...directiveMetadata(type, metadata),
          typeSourceSpan: compiler.createParseSourceSpan(&#39;Component&#39;, type.name, templateUrl),
          template: metadata.template || &#39;&#39;,
          preserveWhitespaces,
          styles: metadata.styles || EMPTY_ARRAY,
          animations: metadata.animations,
          directives: [],
          changeDetection: metadata.changeDetection,
          pipes: new Map(),
          encapsulation,
          interpolation: metadata.interpolation,
          viewProviders: metadata.viewProviders || null,
        };
        // 编译过程需要计算深度,以便确认编译是否最终完成
        compilationDepth++;
        try {
          if (meta.usesInheritance) {
            addDirectiveDefToUndecoratedParents(type);
          }
          // 根据模板、环境和组件需要的元数据,来编译组件
          ngComponentDef = compiler.compileComponent(angularCoreEnv, templateUrl, meta);
        } finally {
          // 即使编译失败,也请确保减少编译深度
          compilationDepth--;
        }
        if (compilationDepth === 0) {
          // 当执行 NgModule 装饰器时,我们将模块定义加入队列,以便仅在所有声明都已解析的情况下才将队列出队,并将其自身作为模块作用域添加到其所有声明中
          // 此调用运行检查以查看队列中的任何模块是否可以出队,并将范围添加到它们的声明中
          flushModuleScopingQueueAsMuchAsPossible();
        }
        // 如果组件编译是异步的,则声明该组件的 @NgModule 批注可以执行并在组件类型上设置 ngSelectorScope 属性
        // 这允许组件在完成编译后,使用模块中的 directiveDefs 对其自身进行修补
        if (hasSelectorScope(type)) {
          const scopes = transitiveScopesFor(type.ngSelectorScope);
          patchComponentDefWithScope(ngComponentDef, scopes);
        }
      }
      return ngComponentDef;
    },
    ...
  });
}

编译组件的过程可能是异步的(比如需要解析组件模板或其他资源的 URL)。如果编译不是立即进行的,compileComponent会将资源解析加入到全局队列中,并且将无法返回ɵcmp,直到通过调用resolveComponentResources解决了全局队列为止。

编译过程中的元数据

元数据是有关类的信息,但它不是类的属性。因此,用于配置类的定义和行为的这些数据,不应该存储在该类的实例中,我们还需要在其他地方保存此数据。

在 Angular 中,编译过程产生的元数据,会使用CompileMetadataResolver来进行管理和维护,这里我们主要看指令(组件)相关的逻辑:

export class CompileMetadataResolver {
  private _nonNormalizedDirectiveCache =
      new Map<Type, {annotation: Directive, metadata: cpl.CompileDirectiveMetadata}>();
  // 使用 Map 的方式来保存
  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) {}
  // 清除特定某个指令的元数据
  clearCacheFor(type: Type) {
    const dirMeta = this._directiveCache.get(type);
    this._directiveCache.delete(type);
    ...
  }
  // 清除所有元数据
  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);
  }
  // 加载指令(组件)元数据
  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)!;
    // 创建指令(组件)元数据
    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);
      }
      // 存储完整的元数据信息,以及元数据摘要信息
      this._directiveCache.set(directiveType, normalizedDirMeta);
      this._summaryCache.set(directiveType, normalizedDirMeta.toSummary());
      return null;
    };

    if (metadata.isComponent) {
      // 如果是组件,该过程可能为异步过程,则需要等待异步过程结束后的模板返回
      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;
      }
      // 并将元数据进行存储
      return SyncAsync.then(templateMeta, createDirectiveMetadata);
    } else {
      // 指令,直接存储元数据
      createDirectiveMetadata(null);
      return null;
    }
  }
  // 获取给定指令(组件)的元数据信息
  getDirectiveMetadata(directiveType: any): cpl.CompileDirectiveMetadata {
    const dirMeta = this._directiveCache.get(directiveType)!;
    ...
    return dirMeta;
  }
  // 获取给定指令(组件)的元数据摘要信息
  getDirectiveSummary(dirType: any): cpl.CompileDirectiveSummary {
    const dirSummary =
        <cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
    ...
    return dirSummary;
  }
}

可以看到,在编译过程中,不管是组件、指令、管道,还是模块,这些类在编译过程中的元数据,都使用Map来存储。

总结

本节我们介绍了 Angular 中的装饰器和元数据,其中元数据用于描述类的定义和行为。

在 Angular 编译过程中,会使用Map的数据结构来维护和存储装饰器的元数据,并根据这些元数据信息来编译组件、指令、管道和模块等。

更多编程相关知识,请访问:编程教学!!

以上がAngular のメタデータとデコレータについて話しましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明
この記事は掘金社区で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。
聊聊Angular中的元数据(Metadata)和装饰器(Decorator)聊聊Angular中的元数据(Metadata)和装饰器(Decorator)Feb 28, 2022 am 11:10 AM

本篇文章继续Angular的学习,带大家了解一下Angular中的元数据和装饰器,简单了解一下他们的用法,希望对大家有所帮助!

angular学习之详解状态管理器NgRxangular学习之详解状态管理器NgRxMay 25, 2022 am 11:01 AM

本篇文章带大家深入了解一下angular的状态管理器NgRx,介绍一下NgRx的使用方法,希望对大家有所帮助!

浅析angular中怎么使用monaco-editor浅析angular中怎么使用monaco-editorOct 17, 2022 pm 08:04 PM

angular中怎么使用monaco-editor?下面本篇文章记录下最近的一次业务中用到的 monaco-editor 在 angular 中的使用,希望对大家有所帮助!

项目过大怎么办?如何合理拆分Angular项目?项目过大怎么办?如何合理拆分Angular项目?Jul 26, 2022 pm 07:18 PM

Angular项目过大,怎么合理拆分它?下面本篇文章给大家介绍一下合理拆分Angular项目的方法,希望对大家有所帮助!

Angular + NG-ZORRO快速开发一个后台系统Angular + NG-ZORRO快速开发一个后台系统Apr 21, 2022 am 10:45 AM

本篇文章给大家分享一个Angular实战,了解一下angualr 结合 ng-zorro 如何快速开发一个后台系统,希望对大家有所帮助!

聊聊自定义angular-datetime-picker格式的方法聊聊自定义angular-datetime-picker格式的方法Sep 08, 2022 pm 08:29 PM

怎么自定义angular-datetime-picker格式?下面本篇文章聊聊自定义格式的方法,希望对大家有所帮助!

聊聊Angular Route中怎么提前获取数据聊聊Angular Route中怎么提前获取数据Jul 13, 2022 pm 08:00 PM

Angular Route中怎么提前获取数据?下面本篇文章给大家介绍一下从 Angular Route 中提前获取数据的方法,希望对大家有所帮助!

浅析Angular中的独立组件,看看怎么使用浅析Angular中的独立组件,看看怎么使用Jun 23, 2022 pm 03:49 PM

本篇文章带大家了解一下Angular中的独立组件,看看怎么在Angular中创建一个独立组件,怎么在独立组件中导入已有的模块,希望对大家有所帮助!

See all articles

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

mPDF

mPDF

mPDF は、UTF-8 でエンコードされた HTML から PDF ファイルを生成できる PHP ライブラリです。オリジナルの作者である Ian Back は、Web サイトから「オンザフライ」で PDF ファイルを出力し、さまざまな言語を処理するために mPDF を作成しました。 HTML2FPDF などのオリジナルのスクリプトよりも遅く、Unicode フォントを使用すると生成されるファイルが大きくなりますが、CSS スタイルなどをサポートし、多くの機能強化が施されています。 RTL (アラビア語とヘブライ語) や CJK (中国語、日本語、韓国語) を含むほぼすべての言語をサポートします。ネストされたブロックレベル要素 (P、DIV など) をサポートします。

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

EditPlus 中国語クラック版

EditPlus 中国語クラック版

サイズが小さく、構文の強調表示、コード プロンプト機能はサポートされていません

VSCode Windows 64 ビットのダウンロード

VSCode Windows 64 ビットのダウンロード

Microsoft によって発売された無料で強力な IDE エディター