目次
増分 DOM
インクリメンタル DOM と仮想 DOM
增量 DOM 元素创建
组件创建与增量 DOM 指令
结束语
ホームページ ウェブフロントエンド jsチュートリアル Angular 学習: Ivy コンパイラーのインクリメンタル DOM の簡単な分析

Angular 学習: Ivy コンパイラーのインクリメンタル DOM の簡単な分析

Feb 23, 2022 am 11:07 AM
angular

この記事は、Angular フレームワークについて学習し、Ivy コンパイラーのインクリメンタル DOM について知っていただくことを目的としています。皆様のお役に立てれば幸いです。

Angular 学習: Ivy コンパイラーのインクリメンタル DOM の簡単な分析

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

フロントエンド フレームワークを導入するときに、テンプレート エンジンを導入することがよくあります。テンプレート エンジンのレンダリング プロセスには、Vue/React などのフレームワークで仮想 DOM などの設計が使用されます。

Angular Ivy コンパイラでは、仮想 DOM は使用されませんが、インクリメンタル DOM が使用されます。

増分 DOM

Ivy コンパイラでは、テンプレート コンパイル済み製品は View エンジンとは異なり、個別のコンパイルや増分コンパイルなどの機能をサポートします。

例: <span>My name is {{name}}</span>このテンプレート コード、Ivy コンパイラでコンパイルされたコードは次のようになります。

// create mode
if (rf & RenderFlags.Create) {
  elementStart(0, "span");
  text(1);
  elementEnd();
}
// update mode
if (rf & RenderFlags.Update) {
  textBinding(1, interpolation1("My name is", ctx.name));
}
ログイン後にコピー

View Engine elementDef(0,null,null,1,'span',...),,elementStart() と比較するとわかります。 , elementEnd()これらの API はより新鮮に見え、インクリメンタル DOM 設計を使用しています。

インクリメンタル DOM と仮想 DOM

仮想 DOM は誰もが理解しているはずです。その中心的な計算プロセスには次のものが含まれます。

  • JavaScript オブジェクトを使用する DOM をシミュレートするツリーを作成し、仮想 DOM ツリーを取得します。

  • ページ データが変更されると、新しい仮想 DOM ツリーが生成され、新旧の仮想 DOM ツリーの差分が比較されます。

  • 差異を実際の DOM ツリーに適用します。

仮想 DOM は、頻繁なページの更新とレンダリングによって引き起こされるパフォーマンスの問題を解決しますが、従来の仮想 DOM には依然として次のようなパフォーマンスのボトルネックがあります。コンポーネントの仮想 DOM ツリー全体をコンポーネント内で走査する必要があります。

    一部のコンポーネントのテンプレート全体に動的ノードが数個しかない場合、これらの走査はパフォーマンスの無駄です
  • 再帰的走査UI レンダリングがブロックされ、ユーザー エクスペリエンスが低下する原因になりやすいです。
  • これらの状況を考慮して、React や Vue などのフレームワークもさらに最適化されています。それぞれツリー diff、コンポーネント diff、要素 diff のアルゴリズムを最適化し、状態更新の計算とレンダリングを制御するタスク スケジューリングを導入します。 Vue 3.0では仮想DOMの更新が従来の全体スコープからツリースコープに変更され、ツリー構造によるアルゴリズムの簡素化とパフォーマンスの向上が図られています。
いずれの場合でも、仮想 DOM の設計には避けられない問題があります。各レンダリング操作では、少なくとも変更されたノードを収容できる十分な大きさの新しい仮想 DOM ツリーが割り当てられ、通常はそれよりも大きくなります。このような設計では、メモリ使用量が増加します。大規模な仮想 DOM ツリーで大量の更新が必要な場合、特にメモリに制約のあるモバイル デバイスでは、パフォーマンスが低下する可能性があります。

インクリメンタル DOM 設計の中心的な考え方は次のとおりです。

新しい (仮想) DOM ツリーを作成するときは、既存のツリーに沿って歩き、変更を見つけます。
  • 変更がない場合、メモリは割り当てられません。
  • #変更がある場合は、既存のツリーを変更します (絶対に必要な場合にのみメモリを割り当てます) )、その差分を物理 DOM に適用します。
  • (仮想) が括弧内に置かれているのは、事前計算されたメタ情報を既存の DOM ノードに混合するときに、仮想 DOM ツリーに依存する代わりに物理 DOM ツリーが使用されるためです。十分。
インクリメンタル DOM には、仮想 DOM ベースのアプローチと比較して 2 つの主な利点があります。

インクリメンタル機能により、レンダリング プロセス中のメモリ割り当てが大幅に削減され、パフォーマンスがより予測可能になります

    テンプレートベースのメソッドに簡単にマッピングできます。制御ステートメントとループは、要素および属性の宣言と自由に組み合わせることができます。
  • インクリメンタル DOM の設計は Google によって提案され、オープン ソース ライブラリも提供しています
  • google/incremental-dom
, DOMツリーの更新を表現・適用するためのライブラリです。 JavaScript を使用すると、データを抽出、反復処理し、HTMLElement および Text ノードを生成する呼び出しに変換できます。

しかし、新しい Ivy エンジンはそれを直接使用せず、独自のバージョンを実装します。

Ivy のインクリメンタル DOM

Ivy エンジンはインクリメンタル DOM の概念に基づいています。仮想 DOM メソッドとの違いは、DOM に対して diff 操作がインクリメンタルに実行されることです (つまり、ノードが 1 回だけ)。 ) 仮想 DOM ツリー上で実行する代わりに。この設計に基づいて、Angular のインクリメンタル DOM とダーティ チェック メカニズムは実際にうまく連携して機能します。

增量 DOM 元素创建

增量 DOM 的 API 的一个独特功能是它分离了标签的打开(elementStart)和关闭(elementEnd),因此它适合作为模板语言的编译目标,这些语言允许(暂时)模板中的 HTML 不平衡(比如在单独的模板中,打开和关闭的标签)和任意创建 HTML 属性的逻辑。

在 Ivy 中,使用elementStartelementEnd创建一个空的 Element 实现如下(在 Ivy 中,elementStartelementEnd的具体实现便是ɵɵelementStartɵɵelementEnd):

export function ɵɵelement(
  index: number,
  name: string,
  attrsIndex?: number | null,
  localRefsIndex?: number
): void {
  ɵɵelementStart(index, name, attrsIndex, localRefsIndex);
  ɵɵelementEnd();
}
ログイン後にコピー

其中,ɵɵelementStart用于创建 DOM 元素,该指令后面必须跟有ɵɵelementEnd()调用。

export function ɵɵelementStart(
  index: number,
  name: string,
  attrsIndex?: number | null,
  localRefsIndex?: number
): void {
  const lView = getLView();
  const tView = getTView();
  const adjustedIndex = HEADER_OFFSET + index;

  const renderer = lView[RENDERER];
  // 此处创建 DOM 元素
  const native = (lView[adjustedIndex] = createElementNode(
    renderer,
    name,
    getNamespace()
  ));
  // 获取 TNode
  // 在第一次模板传递中需要收集匹配
  const tNode = tView.firstCreatePass ?
      elementStartFirstCreatePass(
          adjustedIndex, tView, lView, native, name, attrsIndex, localRefsIndex) :
      tView.data[adjustedIndex] as TElementNode;
  setCurrentTNode(tNode, true);

  const mergedAttrs = tNode.mergedAttrs;
  // 通过推断的渲染器,将所有属性值分配给提供的元素
  if (mergedAttrs !== null) {
    setUpAttributes(renderer, native, mergedAttrs);
  }
  // 将 className 写入 RElement
  const classes = tNode.classes;
  if (classes !== null) {
    writeDirectClass(renderer, native, classes);
  }
  // 将 cssText 写入 RElement
  const styles = tNode.styles;
  if (styles !== null) {
    writeDirectStyle(renderer, native, styles);
  }

  if ((tNode.flags & TNodeFlags.isDetached) !== TNodeFlags.isDetached) {
    // 添加子元素
    appendChild(tView, lView, native, tNode);
  }

  // 组件或模板容器的任何直接子级,必须预先使用组件视图数据进行猴子修补
  // 以便稍后可以使用任何元素发现实用程序方法检查元素
  if (getElementDepthCount() === 0) {
    attachPatchData(native, lView);
  }
  increaseElementDepthCount();

  // 对指令 Host 的处理
  if (isDirectiveHost(tNode)) {
    createDirectivesInstances(tView, lView, tNode);
    executeContentQueries(tView, tNode, lView);
  }
  // 获取本地名称和索引的列表,并将解析的本地变量值按加载到模板中的相同顺序推送到 LView
  if (localRefsIndex !== null) {
    saveResolvedLocalsInData(lView, tNode);
  }
}
ログイン後にコピー

可以看到,在ɵɵelementStart创建 DOM 元素的过程中,主要依赖于LViewTViewTNode

在 Angular Ivy 中,使用了LViewTView.data来管理和跟踪渲染模板所需要的内部数据。对于TNode,在 Angular 中则是用于在特定类型的所有模板之间共享的特定节点的绑定数据(享元)。

ɵɵelementEnd()则用于标记元素的结尾:

export function ɵɵelementEnd(): void {}
ログイン後にコピー

对于ɵɵelementEnd()的详细实现不过多介绍,基本上主要包括一些对 Class 和样式中@input等指令的处理,循环遍历提供的tNode上的指令、并将要运行的钩子排入队列,元素层次的处理等等。

组件创建与增量 DOM 指令

在增量 DOM 中,每个组件都被编译成一系列指令。这些指令创建 DOM 树并在数据更改时就地更新它们。

Ivy 在运行时编译一个组件的过程中,会创建模板解析相关指令:

export function compileComponentFromMetadata(
  meta: R3ComponentMetadata,
  constantPool: ConstantPool,
  bindingParser: BindingParser
): R3ComponentDef {
  // 其他暂时省略

  // 创建一个 TemplateDefinitionBuilder,用于创建模板相关的处理
  const templateBuilder = new TemplateDefinitionBuilder(
      constantPool, BindingScope.createRootScope(), 0, templateTypeName, null, null, templateName,
      directiveMatcher, directivesUsed, meta.pipes, pipesUsed, R3.namespaceHTML,
      meta.relativeContextFilePath, meta.i18nUseExternalIds);

  // 创建模板解析相关指令,包括:
  // 第一轮:创建模式,包括所有创建模式指令(例如解析侦听器中的绑定)
  // 第二轮:绑定和刷新模式,包括所有更新模式指令(例如解析属性或文本绑定)
  const templateFunctionExpression = templateBuilder.buildTemplateFunction(template.nodes, []);

  // 提供这个以便动态生成的组件在实例化时,知道哪些投影内容块要传递给组件
  const ngContentSelectors = templateBuilder.getNgContentSelectors();
  if (ngContentSelectors) {
    definitionMap.set("ngContentSelectors", ngContentSelectors);
  }

  // 生成 ComponentDef 的 consts 部分
  const { constExpressions, prepareStatements } = templateBuilder.getConsts();
  if (constExpressions.length > 0) {
    let constsExpr: o.LiteralArrayExpr|o.FunctionExpr = o.literalArr(constExpressions);
    // 将 consts 转换为函数
    if (prepareStatements.length > 0) {
      constsExpr = o.fn([], [...prepareStatements, new o.ReturnStatement(constsExpr)]);
    }
    definitionMap.set("consts", constsExpr);
  }

  // 生成 ComponentDef 的 template 部分
  definitionMap.set("template", templateFunctionExpression);
}
ログイン後にコピー

可见,在组件编译时,会被编译成一系列的指令,包括constvarsdirectivespipesstyleschangeDetection等等,当然也包括template模板里的相关指令。最终生成的这些指令,会体现在编译后的组件中,比如之前文章中提到的这样一个Component文件:

import { Component, Input } from "@angular/core";

@Component({
  selector: "greet",
  template: "<div> Hello, {{name}}! </div>",
})
export class GreetComponent {
  @Input() name: string;
}
ログイン後にコピー

ngtsc编译后,产物包括该组件的.js文件:

const i0 = require("@angular/core");
class GreetComponent {}
GreetComponent.ɵcmp = i0.ɵɵdefineComponent({
  type: GreetComponent,
  tag: "greet",
  factory: () => new GreetComponent(),
  template: function (rf, ctx) {
    if (rf & RenderFlags.Create) {
      i0.ɵɵelementStart(0, "div");
      i0.ɵɵtext(1);
      i0.ɵɵelementEnd();
    }
    if (rf & RenderFlags.Update) {
      i0.ɵɵadvance(1);
      i0.ɵɵtextInterpolate1("Hello ", ctx.name, "!");
    }
  },
});
ログイン後にコピー

其中,elementStart()text()elementEnd()advance()textInterpolate1()这些都是增量 DOM 相关的指令。在实际创建组件的时候,其template模板函数也会被执行,相关的指令也会被执行。

正因为在 Ivy 中,是由组件来引用着相关的模板指令。如果组件不引用某个指令,则我们的 Angular 中永远不会使用到它。因为组件编译的过程发生在编译过程中,因此我们可以根据引用到指令,来排除未引用的指令,从而可以在 Tree-shaking 过程中,将未使用的指令从包中移除,这便是增量 DOM 可树摇的原因。

结束语

现在,我们已经知道在 Ivy 中,是通过编译器将模板编译为template渲染函数,其中会将对模板的解析编译成增量 DOM 相关的指令。其中,在elementStart()执行时,我们可以看到会通过createElementNode()方法来创建 DOM。实际上,增量 DOM 的设计远不止只是创建 DOM,还包括变化检测等各种能力,关于具体的渲染过程,我们会在下一讲中进行介绍。

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

以上がAngular 学習: Ivy コンパイラーのインクリメンタル DOM の簡単な分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホット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ヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

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

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

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

Angular のメタデータとデコレータについて話しましょう Angular のメタデータとデコレータについて話しましょう Feb 28, 2022 am 11:10 AM

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

Ubuntu 24.04 に Angular をインストールする方法 Ubuntu 24.04 に Angular をインストールする方法 Mar 23, 2024 pm 12:20 PM

Angular.js は、動的アプリケーションを作成するための無料でアクセスできる JavaScript プラットフォームです。 HTML の構文をテンプレート言語として拡張することで、アプリケーションのさまざまな側面を迅速かつ明確に表現できます。 Angular.js は、コードの作成、更新、テストに役立つさまざまなツールを提供します。さらに、ルーティングやフォーム管理などの多くの機能も提供します。このガイドでは、Ubuntu24 に Angular をインストールする方法について説明します。まず、Node.js をインストールする必要があります。 Node.js は、ChromeV8 エンジンに基づく JavaScript 実行環境で、サーバー側で JavaScript コードを実行できます。ウブにいるために

Angular Learning State Manager NgRx の詳細な説明 Angular Learning State Manager NgRx の詳細な説明 May 25, 2022 am 11:01 AM

この記事では、Angular のステートマネージャー NgRx について深く理解し、NgRx の使用方法を紹介します。

Angular で Monaco エディターを使用する方法の簡単な分析 Angular で Monaco エディターを使用する方法の簡単な分析 Oct 17, 2022 pm 08:04 PM

Angularでモナコエディタを使用するにはどうすればよいですか?以下の記事は、最近業務で使用したangularでのmonaco-editorの使い方を記録したものですので、皆様のお役に立てれば幸いです。

Angular のサーバーサイド レンダリング (SSR) について説明する記事 Angular のサーバーサイド レンダリング (SSR) について説明する記事 Dec 27, 2022 pm 07:24 PM

Angular Universal をご存知ですか?これは、Web サイトがより優れた SEO サポートを提供するのに役立ちます。

Angular + NG-ZORRO でバックエンド システムを迅速に開発 Angular + NG-ZORRO でバックエンド システムを迅速に開発 Apr 21, 2022 am 10:45 AM

この記事では、Angular の実践的な経験を共有し、angualr と ng-zorro を組み合わせてバックエンド システムを迅速に開発する方法を学びます。

フロントエンド開発に PHP と Angular を使用する方法 フロントエンド開発に PHP と Angular を使用する方法 May 11, 2023 pm 04:04 PM

インターネットの急速な発展に伴い、フロントエンド開発テクノロジーも常に改善され、反復されています。 PHP と Angular は、フロントエンド開発で広く使用されている 2 つのテクノロジーです。 PHP は、フォームの処理、動的ページの生成、アクセス許可の管理などのタスクを処理できるサーバー側スクリプト言語です。 Angular は、単一ページ アプリケーションの開発やコンポーネント化された Web アプリケーションの構築に使用できる JavaScript フレームワークです。この記事では、PHPとAngularをフロントエンド開発に使用する方法と、それらを組み合わせる方法を紹介します。

プロジェクトが大きすぎる場合はどうすればよいですか? Angular プロジェクトを合理的に分割するにはどうすればよいでしょうか? プロジェクトが大きすぎる場合はどうすればよいですか? Angular プロジェクトを合理的に分割するにはどうすればよいでしょうか? Jul 26, 2022 pm 07:18 PM

Angular プロジェクトが大きすぎます。適切に分割するにはどうすればよいですか?次の記事では、Angular プロジェクトを合理的に分割する方法を紹介します。

See all articles