Angularへの移行に関するストーリーの最後の部分では、開発者が新しいフレームワークに慣れるのに役立つ内部ドキュメントの選択された場所を共有します。 新しいコンポーネントコンパイルロジックの機能、変更検出、およびトランスクルージョンの概念について説明します。 これらは、Angularで作業するときに現在使用されている現在の規則です。 さて、最後に-同僚におすすめする英語の記事やビデオへのリンク。
前のシリーズ: 最初は移行の準備について 、 2番目はハイブリッドモードで作業する機能についてです 。
アプリケーションとAoTのコンパイル
Angularの主な変更点の1つは、コンポーネントのコンパイルロジックです。
AngularJSでは、最初のコンポーネント(一番上のコンポーネント)のHTMLを取得し、DOMに詰め、ブラウザーがそれを解析するか、DOMフラグメントを構築するか、DOMに挿入するか、受信したDOMフラグメントへのリンクを取得し、その中の角形部分を解析します(ディレクティブ) 、コンポーネント)、見つかった各コンポーネントに対して繰り返されました。
Angularは、js / tsのテンプレートのコンパイルを2つのバリアント(JiTおよびAoT)で使用します。 アプリケーションの開始時に(JiTモードで)、フレームはHTMLを含む文字列からすべてのコンポーネントのすべての(一般的にすべての)テンプレートを取得してコンパイルし、目的のDOMピースを作成します。 つまり DOMフラグメントの解析と構築はブラウザにまったく依存せず、Angularは自分で行います。 その後、最上位のコンポーネントから開始して、対応する既製のJSコードの実行を開始します。これにより、DOMの断片が作成され、ツリー内の目的の場所にすぐに挿入されます。 挿入後、追加の解析は必要ありません。 コンパイラはすでにすべてを解析しています。
AoTコンパイルは、アセンブリ中にサーバー上でローカルに実行されます。少し時間がかかりますが、出力はTSファイルの形式で別のディレクトリにあるすべてのコンポーネントのデータを提供します。 それらはメインアプリケーションにしがみつき、TypeScriptのコンパイルはすでにコピーされたテンプレートを考慮しています。
AoTモードでは、テンプレートはJSではなくTypeScriptでコンパイルされます。 これにより、すべてのパターンの静的な型指定が可能になります。 変数をスキップし、プライベートとして指定したか、不適切に処理しました(文字列を待機しているコンポーネントの入力にオブジェクトを押し込もうとした)-アセンブリ中にエラーが発生しました。 ただし、これは実稼働用のビルドモードのみです。 大量の型付きコードが追加されたため、アセンブリ(コンパイルts-> js)が少し遅くなり、開発中にAoTモードがオフになります(マルチスレッドコンパイルの楽しい時や、AoTを開発モードで使用するための苦労を待っています)。
トピックに関するリンク: first 、 secondおよびthird 。
変更検出(CD)
Angularにはグローバルダイジェストサイクル(および変更可能なパラメータのリストがあり、チェックする必要があります)がなくなり、ネイティブイベントバインディング( addEventListener
など)が最大限に使用されますが、検出エンジン自体は消えていません。
個別のイベントディレクティブとタイムアウト/間隔の特別なサービスなしで何かが起こったことをAngularはどのように知っていますか?
Angularの別個のバージョンがあるDart言語から、ゾーンの有用な概念を借用しました。これにより、特定のゾーンでコードの実行が開始されたタイミングと、非同期呼び出しを考慮して終了したタイミングを知ることができます。 これはすべてzone.js libで形になりました 。これにより、このようなゾーンを作成/操作でき、Angularエンジン内で積極的に使用されます。 非同期イベントに遅れないようにするために、zone.jsは対応するメソッド-addEventListener、 setTimeout
、 setInterval
、 Promise
メソッドなどをデコイ(そのバージョンに置き換え)しsetInterval
。 これにより、イベント/追加サービスにバインドするための個別のディレクティブが不要になりました。 したがって、zone.jsでキャッチされたイベントの最後に、Angularは変更検出をプルして、モデルをDOMと同期します。 開発モードでは、検出は2回作動します。2回目は、1回目以降は何も変更されていないことを確認するか、変更された場合は(コンソールのエラー)を与えます。 本番環境では、常に1回だけです(ダイジェストサイクルと対応するエラーの呼び出しが10倍になりません)。
チェックされたパラメータのグローバルプールがない場合、変更検出はどのように行われますか
これは、コンポーネントの作業です。 それぞれに、DOMまたは特別なバインディング( HostBinding
など)で何らかの形で使用されるパラメーターのセットをHostBinding
ことができます。 そのようなパラメータは、コンポーネント内の個別のリストに格納されます(他のどこにも知られていない)。
Angularがグローバル検出変更ログを取得すると、各コンポーネント(ツリーの上部から下部)自体がパラメーターのチェックに関与します。 同時に、変更検出メカニズムをコンポーネントから切り離すことができ、そのパラメーターは原則としてチェックされません。 または、入力の変更のみをチェックするように構成することができ( ChangeDetectionStrategy.OnPush
)、何もチェックしないだけでなく、それに含まれるコンポーネント/ディレクティブのチェックの開始を完全に遮断します。 したがって、 OnPush
が正しく分散されているため、各ティックのスキャンからコンポーネントのブランチ全体を除外できます。
また、Angularゾーンの外部または(明示的に)コードの一部を実行することもできます(Angularゾーンでは、イベントなどをキャッチします)。 これは、これらすべてのトラブルをごまかすためにサードパーティ製のライブラリに固執する場合に役立ちます。 また、ライブラリ内のすべてのイベントやタイムアウトに対して、コンポーネントツリー全体でけいれんが発生することはありません。 後でデータを更新したことを角度に伝えるために(彼が聞いていたゾーンの外部で起動されたこのlibから取得)、角度ゾーンで何らかのアクション(たとえば、コンポーネントのローカルプロパティの変更)の実行を明示的に規定します。
detectChangesおよびmarkForCheck
分離されたCDまたはコンポーネント(ツリーの現在のコンポーネント以上)にChangeDetectionStrategy.OnPush
、 ChangeDetectionStrategy.OnPush
書き込まれ、自動的に取得されない変更がある場合、ローカルのChangeDetectorRef
サービスには2つのメソッドがありますdetectChanges
およびmarkForCheck
:
-
markForCheck
:現在のコンポーネント(包括的)からツリーのルートコンポーネントまで、すべてのビューをグローバルCDの最も近いティックのチェックが必要であるとしてマークします。 ティック自体は同時にけいれんせず、他の誰かがこのティックをプルした場合(または既にプルされているが、変更が安定するのを待っている場合)にのみビューがチェックされます。 -
detectChanges
:検出器/入力の状態に関係なく、現在のコンポーネントのビューに対してのみCDを引き出します。 すべての子について、標準ロジックが機能しますOnPush
がある場合、入力を確認し、変更検出器が切断されている場合、何も監視せず、無視します。 作業の特徴-コンポーネントが破壊の過程にある(または破壊されていると見なされる)場合、そのビューが切断されると、detectChanges
呼び出しdetectChanges
その瞬間にエラーdetectChanges
スローします(たとえば、タイムアウトでプルすることを決定し、コンポーネントはルートの変更によりすでに破壊され、CDをプルします)存在しないビューで)。 glyいハック-既にキルされたビューを確認します(そのような場合は避けてください):
if (!this.changeDetectorRef["destroyed"]) { this.changeDetectorRef.detectChanges(); }
使用する場合:
-
OnPush
を備えたコンポーネントがあり、変更が「合法的な」方法で行われた場合(apiから、タイムアウト、グローバルCDの目盛りのあるゾーン内で好きなように)、markForCheck
使用しmarkForCheck
。 - コンポーネントにデタッチされたCDがある場合、または何かが角度ゾーンの外側で変更された場合(明らかにグローバルCDを引っ張った人がいない場合)、
detectChanges
を使用し、コンポーネントが破壊される可能性があることを忘れないでください。
ng-content
- ng-transclude
と同じではありません
Angularのトランスクラッジの概念は少し変更されました(これがWebコンポーネントでどのように行われるか、およびエンジン/コンパイラの機能の影響を受けて)。 外部では、 ng-content
はng-transclude
と同じ機能を持ち、コンポーネントに何かを投げて、特定の場所に投げselect
(属性をselect
ます)。
ただし、1つの小さな違いがありますng-content
content内にデフォルトのコンテンツを指定する方法はありません-非常に重要なものです:このコンテンツをスローする人は、 ng-content
タグを含むコンポーネントではなく、 ng-content
ロールされるコンテンツの初期化を担当します。
例の説明:
@Component({ ... template: ` Visible: {{ visible }} <ng-content *ngIf="visible"></ng-content> `, }) class ProjectionComponent implements OnInit { public visible = false; public ngOnInit() { window.setTimeout(() => this.visible = true, 1000); window.setTimeout(() => this.visible = false, 2000); } } @Component({...}) class ChildComponent implements OnInit, AfterViewInit, OnDestroy { public ngOnInit() { console.log("Child component ngOnInit"); } public ngAfterViewInit() { console.log("Child component ngAfterViewInit"); } public ngOnDestroy() { console.log("Child component ngOnDestroy"); } } @Component({ ... template: ` <vim-projection> <vim-child-component></vim-child-component> </vim-projection> `, }) class ParentComponent {}
ProjectionComponent
とコンソールログでngIf
:
// `Visible` `ProjectionComponent` Child component ngOnInit // Child component ngAfterViewInit // 2 // …
最初の1秒では、 *ngIf="false"
にもかかわらず、 ChildComponent
が初期化され( OnInit
フックが機能)、 OnDestroy
は2秒後にOnDestroy
しませんでしたが、コンポーネントは非表示になったようです。
つまり ng-content
を含むコンポーネントで何が起こっても、そこにスローされるものはすべて初期化されます( OnInit
フックが呼び出されます)。 これは、コンポーネントの初期化で要求、重い処理などがある場合に重要です。
そのようなコンテンツを非表示にする必要がある場合は、それをスローする人がこれを行う必要があります。
<vim-projection> <vim-child-component *ngIf="visibleInParent"></vim-child-component> </vim-projection>
または、TemplateRefを使用してこの制限を回避する興味深い方法を使用します。
<div *ngIf="condition" class="smth1"> <ng-container *ngTemplateOutlet="contentTpl"></ng-container> </div> <div *ngIf="!condition" class="smth2"> <ng-container *ngTemplateOutlet="contentTpl"></ng-container> </div> <div *ngIf="condition2" class="smth1"> <ng-container *ngTemplateOutlet="contentWithSelectorTpl"></ng-container> </div> <div *ngIf="!condition2" class="smth2"> <ng-container *ngTemplateOutlet="contentWithSelectorTpl"></ng-container> </div> <ng-template #contentTpl><ng-content></ng-content></ng-template> <ng-template #contentWithSelectorTpl><ng-content select="[some-attribute]"></ng-content></ng-template>
制限事項-このようなng-content
を一度に挿入できるのは1箇所のみです。
また、 ng-content
では、テンプレートに複数の同一のスロットを指定することはできません。同じスロットを表示すると、条件がハングします。
<!-- --> <div *ngIf="smth" class="smth1"> <ng-content></ng-content> </div> <div *ngIf="!smth" class="smth2"> <ng-content></ng-content> </div> <!-- --> <div *ngIf="smth" class="smth1"> <ng-content select="[some-selector]"></ng-content> </div> <div *ngIf="!smth" class="smth2"> <ng-content select="[some-selector]"></ng-content> </div>
有用な資料へのリンク
Googleでは、角度2+に多くのことがあります。すぐに/ドックで不明なトピックを安全に検索できます。 リリースから(そしてそれ以前)人々はフレームをパーツに分解し、それがどのように機能するかを理解していました。
便利なリンクを次に示します。
ElementRef、TemplateRef、ViewContainerRef
ViewChildren、ContentChildren、QueryList
そして最後に、いつものように、 Angularだけでなくクールな開発者を常に探していることを思い出させてください。