この記事は、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 の設計は Google によって提案され、オープン ソース ライブラリも提供しています google/incremental-dom
しかし、新しい Ivy エンジンはそれを直接使用せず、独自のバージョンを実装します。
Ivy のインクリメンタル DOMIvy エンジンはインクリメンタル DOM の概念に基づいています。仮想 DOM メソッドとの違いは、DOM に対して diff 操作がインクリメンタルに実行されることです (つまり、ノードが 1 回だけ)。 ) 仮想 DOM ツリー上で実行する代わりに。この設計に基づいて、Angular のインクリメンタル DOM とダーティ チェック メカニズムは実際にうまく連携して機能します。增量 DOM 元素创建
增量 DOM 的 API 的一个独特功能是它分离了标签的打开(elementStart
)和关闭(elementEnd
),因此它适合作为模板语言的编译目标,这些语言允许(暂时)模板中的 HTML 不平衡(比如在单独的模板中,打开和关闭的标签)和任意创建 HTML 属性的逻辑。
在 Ivy 中,使用elementStart
和elementEnd
创建一个空的 Element 实现如下(在 Ivy 中,elementStart
和elementEnd
的具体实现便是ɵɵ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 元素的过程中,主要依赖于LView
、TView
和TNode
。
在 Angular Ivy 中,使用了LView
和TView.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); }
可见,在组件编译时,会被编译成一系列的指令,包括const
、vars
、directives
、pipes
、styles
、changeDetection
等等,当然也包括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 サイトの他の関連記事を参照してください。

現実世界でのJavaScriptのアプリケーションには、フロントエンドとバックエンドの開発が含まれます。 1)DOM操作とイベント処理を含むTODOリストアプリケーションを構築して、フロントエンドアプリケーションを表示します。 2)node.jsを介してRestfulapiを構築し、バックエンドアプリケーションをデモンストレーションします。

Web開発におけるJavaScriptの主な用途には、クライアントの相互作用、フォーム検証、非同期通信が含まれます。 1)DOM操作による動的なコンテンツの更新とユーザーインタラクション。 2)ユーザーエクスペリエンスを改善するためにデータを提出する前に、クライアントの検証が実行されます。 3)サーバーとのリフレッシュレス通信は、AJAXテクノロジーを通じて達成されます。

JavaScriptエンジンが内部的にどのように機能するかを理解することは、開発者にとってより効率的なコードの作成とパフォーマンスのボトルネックと最適化戦略の理解に役立つためです。 1)エンジンのワークフローには、3つの段階が含まれます。解析、コンパイル、実行。 2)実行プロセス中、エンジンはインラインキャッシュや非表示クラスなどの動的最適化を実行します。 3)ベストプラクティスには、グローバル変数の避け、ループの最適化、constとletsの使用、閉鎖の過度の使用の回避が含まれます。

Pythonは、スムーズな学習曲線と簡潔な構文を備えた初心者により適しています。 JavaScriptは、急な学習曲線と柔軟な構文を備えたフロントエンド開発に適しています。 1。Python構文は直感的で、データサイエンスやバックエンド開発に適しています。 2。JavaScriptは柔軟で、フロントエンドおよびサーバー側のプログラミングで広く使用されています。

PythonとJavaScriptには、コミュニティ、ライブラリ、リソースの観点から、独自の利点と短所があります。 1)Pythonコミュニティはフレンドリーで初心者に適していますが、フロントエンドの開発リソースはJavaScriptほど豊富ではありません。 2)Pythonはデータサイエンスおよび機械学習ライブラリで強力ですが、JavaScriptはフロントエンド開発ライブラリとフレームワークで優れています。 3)どちらも豊富な学習リソースを持っていますが、Pythonは公式文書から始めるのに適していますが、JavaScriptはMDNWebDocsにより優れています。選択は、プロジェクトのニーズと個人的な関心に基づいている必要があります。

C/CからJavaScriptへのシフトには、動的なタイピング、ゴミ収集、非同期プログラミングへの適応が必要です。 1)C/Cは、手動メモリ管理を必要とする静的に型付けられた言語であり、JavaScriptは動的に型付けされ、ごみ収集が自動的に処理されます。 2)C/Cはマシンコードにコンパイルする必要がありますが、JavaScriptは解釈言語です。 3)JavaScriptは、閉鎖、プロトタイプチェーン、約束などの概念を導入します。これにより、柔軟性と非同期プログラミング機能が向上します。

さまざまなJavaScriptエンジンは、各エンジンの実装原則と最適化戦略が異なるため、JavaScriptコードを解析および実行するときに異なる効果をもたらします。 1。語彙分析:ソースコードを語彙ユニットに変換します。 2。文法分析:抽象的な構文ツリーを生成します。 3。最適化とコンパイル:JITコンパイラを介してマシンコードを生成します。 4。実行:マシンコードを実行します。 V8エンジンはインスタントコンピレーションと非表示クラスを通じて最適化され、Spidermonkeyはタイプ推論システムを使用して、同じコードで異なるパフォーマンスパフォーマンスをもたらします。

現実世界におけるJavaScriptのアプリケーションには、サーバー側のプログラミング、モバイルアプリケーション開発、モノのインターネット制御が含まれます。 2。モバイルアプリケーションの開発は、ReactNativeを通じて実行され、クロスプラットフォームの展開をサポートします。 3.ハードウェアの相互作用に適したJohnny-Fiveライブラリを介したIoTデバイス制御に使用されます。


ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

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

人気の記事

ホットツール

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

SublimeText3 英語版
推奨: Win バージョン、コードプロンプトをサポート!

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

Dreamweaver Mac版
ビジュアル Web 開発ツール

VSCode Windows 64 ビットのダウンロード
Microsoft によって発売された無料で強力な IDE エディター
