インクリメンタルハイドレーションは、アプリケーションの一部を非ハイドレーション状態のままにし、必要に応じてそれらのセクションのハイドレーションを段階的にトリガーできる、高度なタイプのハイドレーションです。
インクリメンタルハイドレーションを使用する理由
インクリメンタルハイドレーションは、完全なアプリケーションハイドレーションを基盤としたパフォーマンス向上策です。完全なアプリケーションハイドレーションと同等のエンドユーザー体験を提供しながら、より小さな初期バンドルを作成できます。バンドルサイズが小さくなると、初期ロード時間が短縮され、First Input Delay (FID)とCumulative Layout Shift (CLS)が改善されます。
インクリメンタルハイドレーションを使用すると、以前は遅延できなかった可能性のあるコンテンツに遅延可能ビュー(@defer)を使用できるようになります。具体的には、画面の上部に表示されるコンテンツに遅延可能ビューを使用できるようになりました。インクリメンタルハイドレーション以前は、画面の上部に@deferブロックを配置すると、プレースホルダーコンテンツがレンダリングされ、その後@deferブロックのメインテンプレートコンテンツに置き換えられます。これにより、レイアウトシフトが発生します。インクリメンタルハイドレーションを使用すると、@deferブロックのメインテンプレートは、ハイドレーション時にレイアウトシフトなしでレンダリングされます。
Angularでインクリメンタルハイドレーションを有効にする方法
ハイドレーションによるサーバーサイドレンダリング(SSR)を既に使用しているアプリケーションでインクリメンタルハイドレーションを有効にできます。サーバーサイドレンダリングを有効にするにはAngular SSRガイドを、ハイドレーションを有効にするにはAngularハイドレーションガイドを参照してください。
provideClientHydrationプロバイダーにwithIncrementalHydration()関数を追加することで、インクリメンタルハイドレーションを有効にします。
import { bootstrapApplication, provideClientHydration, withIncrementalHydration,} from '@angular/platform-browser';...bootstrapApplication(AppComponent, { providers: [provideClientHydration(withIncrementalHydration())]});
インクリメンタルハイドレーションはイベントリプレイに依存し、自動的に有効にします。既にリストにwithEventReplay()がある場合は、インクリメンタルハイドレーションを有効にした後、安全に削除できます。
インクリメンタルハイドレーションの動作方法
インクリメンタルハイドレーションは、完全なアプリケーションハイドレーション、遅延可能ビュー、およびイベントリプレイを基盤として構築されています。インクリメンタルハイドレーションを使用すると、インクリメンタルハイドレーションの境界を定義する@deferブロックに追加のトリガーを追加できます。deferブロックにhydrateトリガーを追加すると、Angularはサーバーサイドレンダリング中にそのdeferブロックの依存関係をロードし、@placeholderではなくメインテンプレートをレンダリングする必要があることを認識します。クライアントサイドレンダリングの場合、依存関係はまだ遅延され、hydrateトリガーが起動するまでdeferブロックのコンテンツは非ハイドレーション状態のままです。そのトリガーは、deferブロックに依存関係を取得してコンテンツをハイドレーションするよう指示します。ハイドレーションの前にユーザーによってトリガーされたブラウザイベント、特にコンポーネントに登録されたリスナーと一致するイベントは、キューに入れられ、ハイドレーションプロセスが完了すると再生されます。
トリガーによるコンテンツのハイドレーション制御
Angularが遅延コンテンツをロードしてハイドレーションするタイミングを制御するハイドレーショントリガーを指定できます。これらは、通常の@deferトリガーとともに使用できる追加のトリガーです。
各@deferブロックには、セミコロン(;)で区切られた複数のハイドレーションイベントトリガーを含めることができます。Angularは、いずれかのトリガーが起動するとハイドレーションを開始します。
ハイドレーショントリガーには、hydrate on、hydrate when、hydrate neverの3種類があります。
hydrate on
hydrate onは、@deferブロックのハイドレーションがトリガーされる条件を指定します。
使用可能なトリガーは次のとおりです。
| トリガー | 説明 |
|---|---|
hydrate on idle |
ブラウザがアイドル状態になったときにトリガーされます。 |
hydrate on viewport |
指定されたコンテンツがビューポートに入ったときにトリガーされます。 |
hydrate on interaction |
ユーザーが指定された要素と対話したときにトリガーされます。 |
hydrate on hover |
マウスが指定された領域にホバーしたときにトリガーされます。 |
hydrate on immediate |
非遅延コンテンツのレンダリングが完了した直後にトリガーされます。 |
hydrate on timer |
特定の期間後にトリガーされます。 |
hydrate on idle
hydrate on idleトリガーは、requestIdleCallbackに基づいてブラウザがアイドル状態に達すると、遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。
@defer (hydrate on idle) { <large-cmp />} @placeholder { <div>Large component placeholder</div>}
hydrate on viewport
hydrate on viewportトリガーは、Intersection Observer APIを使用して指定されたコンテンツがビューポートに入ったときに、
対応するアプリケーションのページの遅延可能ビューの依存関係をロードし、ハイドレーションします。
@defer (hydrate on viewport) { <large-cmp />} @placeholder { <div>Large component placeholder</div>}
hydrate on interaction
hydrate on interactionトリガーは、ユーザーがclickまたはkeydownイベントを通じて指定された要素と対話したときに、
遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。
@defer (hydrate on interaction) { <large-cmp />} @placeholder { <div>Large component placeholder</div>}
hydrate on hover
hydrate on hoverトリガーは、mouseoverおよびfocusinイベントを通じてマウスがトリガー領域にホバーしたときに、
遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。
@defer (hydrate on hover) { <large-cmp />} @placeholder { <div>Large component placeholder</div>}
hydrate on immediate
hydrate on immediateトリガーは、遅延可能ビューの依存関係をロードし、すぐにコンテンツをハイドレーションします。
これは、他のすべての非遅延コンテンツのレンダリングが完了するとすぐに、遅延ブロックがロードされることを意味します。
@defer (hydrate on immediate) { <large-cmp />} @placeholder { <div>Large component placeholder</div>}
hydrate on timer
hydrate on timerトリガーは、指定された期間後に遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。
@defer (hydrate on timer(500ms)) { <large-cmp />} @placeholder { <div>Large component placeholder</div>}
期間パラメーターは、ミリ秒(ms)または秒(s)で指定する必要があります。
hydrate when
hydrate whenトリガーはカスタムの条件式を受け入れ、条件が真になったときに
遅延可能ビューの依存関係をロードし、コンテンツをハイドレーションします。
@defer (hydrate when condition) { <large-cmp />} @placeholder { <div>Large component placeholder</div>}
NOTE: hydrate when条件は、最上位の非ハイドレーション@deferブロックである場合にのみトリガーされます。
トリガーに提供される条件は親コンポーネントで指定され、トリガーされる前に存在する必要があります。
親ブロックが非ハイドレーション状態の場合、その式はまだAngularによって解決できません。
hydrate never
hydrate neverを使用すると、deferブロック内のコンテンツを無期限に非ハイドレーション状態のままにでき、事実上静的コンテンツになります。
これは最初のレンダリングにのみ適用されることに注意してください。
後続のクライアントサイドレンダリングでは、hydrate neverを含む@deferブロックは依存関係をロードします。ハイドレーションはサーバーサイドレンダリングされたコンテンツの最初のロードにのみ適用されるためです。
次の例では、後続のクライアントサイドレンダリングでは、ビューポートで@deferブロックの依存関係がロードされます。
@defer (on viewport; hydrate never) { <large-cmp />} @placeholder { <div>Large component placeholder</div>}
NOTE: hydrate neverを使用すると、指定された@deferブロックのネストされたサブツリー全体のハイドレーションが防止されます。そのブロックの下にネストされたコンテンツに対しては、他のhydrateトリガーは起動しません。
通常のトリガーとハイドレーショントリガーの併用
ハイドレーショントリガーは、@deferブロックの通常のトリガーと併用される追加のトリガーです。ハイドレーションは最初のロードの最適化であるため、ハイドレーショントリガーはその最初のロードにのみ適用されます。後続のクライアントサイドレンダリングでは、通常のトリガーが引き続き使用されます。
@defer (on idle; hydrate on interaction) { <example-cmp />} @placeholder{ <div>Example Placeholder</div>}
この例では、最初のロード時にhydrate on interactionが適用されます。<example-cmp />コンポーネントとの対話でハイドレーションがトリガーされます。クライアントサイドでレンダリングされる後続のページロード(たとえば、このコンポーネントを含むページをロードするrouterLinkをクリックした場合)では、on idleが適用されます。
ネストされた@deferブロックとインクリメンタルハイドレーションの連携方法
Angularのコンポーネントと依存関係のシステムは階層的であるため、コンポーネントをハイドレーションするには、そのすべての親もハイドレーションされている必要があります。したがって、ネストされた非ハイドレーション@deferブロックのセットの子@deferブロックでハイドレーションがトリガーされた場合、ハイドレーションは最上位の非ハイドレーション@deferブロックからトリガーされた子まで順にトリガーされ、その順序で実行されます。
@defer (hydrate on interaction) { <parent-block-cmp /> @defer (hydrate on hover) { <child-block-cmp /> } @placeholder { <div>Child placeholder</div> }} @placeholder{ <div>Parent Placeholder</div>}
上記の例では、ネストされた@deferブロックにホバーするとハイドレーションがトリガーされます。<parent-block-cmp />を含む親@deferブロックが最初にハイドレーションされ、その後<child-block-cmp />を含む子@deferブロックがハイドレーションされます。
制約事項
インクリメンタルハイドレーションには、完全なアプリケーションハイドレーションと同じ制約事項があり、直接的なDOM操作の制限や有効なHTML構造の必要性などが含まれます。詳細については、ハイドレーションガイドの制約事項セクションを参照してください。
@placeholderブロックはまだ指定する必要がありますか?
はい。@placeholderブロックのコンテンツはインクリメンタルハイドレーションには使用されませんが、後続のクライアントサイドレンダリングの場合には@placeholderが必要です。コンテンツが最初のロードの一部であったルートにない場合、@deferブロックコンテンツを持つルートへのナビゲーションは、通常の@deferブロックのようにレンダリングされます。そのため、@placeholderはこれらのクライアントサイドレンダリングの場合にレンダリングされます。