ASP.NET Core Blazor のレンダー モード

この記事では、コンパイル時または実行時の Blazor Web Apps での Razor コンポーネントのレンダリングの制御について説明します。

このガイダンスは、スタンドアロン Blazor WebAssembly アプリには適用されません。 Blazor WebAssembly アプリは、クライアント側の WebAssembly ベースのランタイムを介してクライアント上でレンダリングのみを行い、レンダリング モードの概念はありません。 レンダリング モードが Blazor WebAssembly アプリのコンポーネントに適用されている場合、レンダリング モードの指定はコンポーネントのレンダリングに影響しません。

レンダー モード

Blazor Web アプリのすべてのコンポーネントは、レンダー モードを採用して、使用するホスティング モデル、レンダリング場所、対話型かどうかを判断します。

次の表は、Blazor Web アプリで Razor コンポーネントをレンダリングするために使用できるレンダー モードを示しています。 コンポーネントにレンダリング モードを適用するには、コンポーネント インスタンスまたはコンポーネント定義で @rendermode ディレクティブを使います。 この記事の後半では、レンダー モードのシナリオごとに例を示します。

名前 説明 レンダーの場所 Interactive
静的サーバー 静的サーバー側レンダリング (静的 SSR) [サーバー] いいえ
対話型サーバー Blazor Server を使用した対話型サーバー側レンダリング (対話型 SSR)。 [サーバー] はい
対話型 WebAssembly Blazor WebAssembly を使用したクライアント側レンダリング (CSR)†。 クライアント はい
対話型自動 最初に Blazor Server、その後 Blazor バンドルのダウンロード後の後続のアクセスに CSR を使用する、対話型 SSR。 サーバー、その後クライアント はい

†クライアント側レンダリング (CSR) は対話型と見なされます。 ""対話型" クライアント側レンダリング" と ""対話型" CSR" は業界や Blazor のドキュメントでは使用されません。

対話型コンポーネントではプリレンダリングが既定で有効になっています。 プリレンダリングの制御に関するガイダンスについては、この記事の後半で説明します。 クライアントとサーバーのレンダリングの概念に関する一般的な業界用語については、「ASP.NET Core Blazor の基礎」をご覧ください。

次の例では、いくつかの基本的な Razor コンポーネント機能を使用してコンポーネントのレンダー モードを設定する方法を示します。

レンダー モードの動作をローカルでテストするには、Blazor Web アプリ プロジェクト テンプレートから作成されたアプリに次のコンポーネントを配置します。 アプリを作成するときに、ドロップダウン メニューからオプション (Visual Studio) を選択するか、CLI オプション (.NET CLI) を適用して、サーバー側とクライアント側の両方のインタラクティビティを有効にします。 Blazor Web アプリの作成方法に関するガイダンスについては、「ASP.NET Core Blazor 用のツール」を参照してください。

対話型レンダー モードのサポートを有効にする

Blazor Web アプリは、対話型レンダー モードをサポートするように構成する必要があります。 次の拡張機能は、アプリの作成時に Blazor Web アプリ プロジェクト テンプレートから作成されたアプリに自動的に適用されます。 個々のコンポーネントは、アプリの Program ファイルでコンポーネント サービスとエンドポイントを構成した後、「レンダー モード」セクションに従ってレンダー モードを宣言する必要があります。

Razor コンポーネントのサービスは、AddRazorComponents を呼び出すことによって追加されます。

コンポーネント ビルダー拡張機能:

  • AddInteractiveServerComponents によって、対話型サーバー コンポーネントのレンダリングをサポートするサービスが追加されます。
  • AddInteractiveWebAssemblyComponents によって、対話型 WebAssembly コンポーネントのレンダリングをサポートするサービスが追加されます。

MapRazorComponents は使用可能なコンポーネントを検出し、アプリのルート コンポーネント (読み込む最初のコンポーネント) を特定します。これは既定では App コンポーネント (App.razor) です。

エンドポイント規則ビルダー拡張機能:

Note

次の例での API の配置についての説明については、Blazor Web アプリ プロジェクト テンプレートから生成されたアプリの Program ファイルを調べてください。 Blazor Web アプリの作成方法に関するガイダンスについては、「ASP.NET Core Blazor 用のツール」を参照してください。

例 1: 次の Program ファイルの API は、対話型 SSR を有効にするためのサービスと構成を追加します。

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();
app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

例 2: 次の Program ファイルの API は、対話型 WebAssembly レンダリング モードを有効にするためのサービスと構成を追加します。

builder.Services.AddRazorComponents()
    .AddInteractiveWebAssemblyComponents();
app.MapRazorComponents<App>()
    .AddInteractiveWebAssemblyRenderMode();

例 3: 次の Program ファイルの API は、対話型サーバー、対話型 WebAssembly、対話型自動レンダリング モードを有効にするためのサービスと構成を追加します。

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents()
    .AddInteractiveWebAssemblyComponents();
app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode()
    .AddInteractiveWebAssemblyRenderMode();

Blazor は、Blazor WebAssembly ホスティング モデルを使用して、対話型 WebAssembly レンダリング モードを使用するコンポーネントをダウンロードして実行します。 これらのコンポーネントの Blazor WebAssembly ホスティングを設定するには、別のクライアント プロジェクトが必要です。 クライアント プロジェクトには、Blazor WebAssembly ホストのスタートアップ コードが含まれており、ブラウザーで実行するための .NET ランタイムを設定します。 Blazor Web アプリ テンプレートでは、WebAssembly のインタラクティビティを有効にするオプションを選択すると、このクライアント プロジェクトが自動的に追加されます。 対話型 WebAssembly レンダリング モードを使用するコンポーネントは、クライアント プロジェクトからビルドする必要があるため、ダウンロードしたアプリ バンドルに含められます。

コンポーネント インスタンスにレンダー モードを適用する

コンポーネント インスタンスにレンダー モードを適用するには、コンポーネントが使用される場所の @rendermodeRazor ディレクティブ属性を使用します。

次の例では、対話型サーバー側レンダリング (対話型 SSR) が Dialog コンポーネント インスタンスに適用されます。

<Dialog @rendermode="InteractiveServer" />

Note

Blazor テンプレートには、より短い @rendermode 構文のためにアプリの _Imports ファイル (Components/_Imports.razor) の RenderMode の静的 using ディレクティブが含まれています。

@using static Microsoft.AspNetCore.Components.Web.RenderMode

上のディレクティブがない場合、コンポーネントは @rendermode 構文で静的な RenderMode クラスを指定する必要があります。

<Dialog @rendermode="RenderMode.InteractiveServer" />

また、カスタムの構成で直接インスタンス化されたカスタム レンダリング モード インスタンスを参照することもできます。 詳しくは、この記事で後述する「カスタムの短縮形レンダリング モード」セクションをご覧ください。

コンポーネント定義にレンダー モードを適用する

コンポーネントのレンダー モードを定義の一部として指定するには、@rendermodeRazor ディレクティブ とそれに対応するレンダー モード属性を使用します。

@page "..."
@rendermode InteractiveServer

コンポーネント定義にレンダー モードを適用することは、特定のページにレンダー モードを適用するときによく使用されます。 ルーティング可能なページは、既定で、そのページをレンダリングした Router コンポーネントと同じレンダー モードを使用します。

技術的には、@rendermode は Razor "ディレクティブ" であり、Razor "ディレクティブ属性" でもあります。 セマンティクスは似ていますが、違いがあります。 @rendermode ディレクティブはコンポーネント定義に対するものなので、参照されるレンダリング モード インスタンスは静的である必要があります。 @rendermode ディレクティブ属性は、任意のレンダリング モード インスタンスを受け取ることができます。

Note

コンポーネント作成者は、コンポーネントの実装を特定のレンダー モードに結合しないようにする必要があります。 代わりに、コンポーネント作成者は通常、任意のレンダー モードまたはホスティング モデルをサポートするようにコンポーネントを設計する必要があります。 コンポーネントの実装では、実行されている場所 (サーバーまたはクライアント) に関する想定を回避し、静的にレンダリングするときは適切に品質を下げる必要があります。 コンポーネントが直接インスタンス化されていない場合 (ルーティング可能なページ コンポーネントなど)、またはすべてのコンポーネント インスタンスのレンダー モードを指定する場合は、コンポーネント定義でレンダー モードを指定することが必要になる場合があります。

アプリ全体にレンダリング モードを適用する

アプリ全体のレンダリング モードを設定するには、アプリのコンポーネント階層の、ルート コンポーネントではない最上位の対話型コンポーネントで、レンダリング モードを指定します。

Note

ルート コンポーネントを対話型にすること (App コンポーネントなど) はサポートされていません。 そのため、アプリ全体のレンダリング モードを App コンポーネントで直接設定することはできません。

Blazor Web アプリ プロジェクト テンプレートに基づくアプリの場合、アプリ全体に割り当てられるレンダリング モードは、通常、App コンポーネント (Components/App.razor) で Routes コンポーネントが使われる場所で指定されます。

<Routes @rendermode="InteractiveServer" />

Router コンポーネントは、ルーティングするページに自身のレンダー モードを伝達します。

また、通常は、HeadOutlet コンポーネントで同じインタラクティブ レンダー モードを設定する必要があります。これは、プロジェクト テンプレートから生成された Blazor Web アプリの App コンポーネントにも含まれます。

<HeadOutlet @rendermode="InteractiveServer" />

対話型クライアント側 (WebAssembly または自動) レンダリング モードを採用し、Routes コンポーネントを介してアプリ全体でレンダリング モードを有効にするアプリの場合:

  • サーバー アプリの Components/Layout フォルダーにあるレイアウト ファイルとナビゲーション ファイルを、.Client プロジェクトの Layout フォルダーに配置または移動します。 .Client プロジェクトに Layout フォルダーが存在しない場合は作成します。
  • サーバー アプリの Components/Pages フォルダーにあるコンポーネントを、.Client プロジェクトの Pages フォルダーに配置または移動します。 .Client プロジェクトに Pages フォルダーが存在しない場合は作成します。
  • サーバー アプリの Components フォルダーにある Routes コンポーネントを、.Client プロジェクトのルート フォルダーに配置または移動します。

Blazor Web アプリの作成時にグローバルな対話機能を有効にするには:

  • Visual Studio: [Interactivity location] (対話機能の場所) ドロップダウン リストを [グローバル] に設定します。
  • .NET CLI: -ai|--all-interactive オプションを使います。

詳しくは、「ASP.NET Core Blazor 用のツール」をご覧ください。

プログラムでレンダリング モードを適用する

プロパティとフィールドで、レンダー モードを割り当てることができます。

このセクションで説明する 2 つ目の方法である、コンポーネント インスタンスによるレンダー モードの設定は、アプリの仕様で次のいずれかのシナリオが必要になる場合に特に便利です。

  • 静的サーバー側レンダリング (静的 SSR) を採用し、サーバー上でのみ実行する必要があるコンポーネントを含むアプリの領域 (フォルダー) がある。 アプリは、フォルダーへのパスに基づいて App コンポーネント内の Routes コンポーネントにレンダー モードを設定することで、レンダー モードをグローバルに制御します。
  • さまざまな場所 (1 つのフォルダーでない) にあるアプリの周辺に、静的 SSR を採用し、サーバー上でのみ実行する必要がコンポーネントがある。 アプリは、コンポーネント インスタンスの @rendermode ディレクティブを使用してレンダー モードを設定することで、コンポーネントごとにレンダー モードを制御します。 App コンポーネントでリフレクションを使用して、Routes コンポーネントのレンダー モードを設定します。

どちらの場合も、静的 SSR を採用する必要があるコンポーネントは、ページ全体の再読み込みも強制する必要があります。

上記の 2 つのシナリオについては、この記事の後半の「レンダー モードの細かい制御」セクションの例で説明します。 次の 2 つのサブセクションでは、レンダー モードを設定するための基本的な方法に焦点を当てます。

コンポーネント定義でレンダー モードを設定する

コンポーネントの定義では、プライベート フィールドを使ってレンダリング モードを定義できます。

@rendermode renderModeForPage

...

@code {
    private static IComponentRenderMode renderModeForPage = InteractiveServer;
}

コンポーネント インスタンスでレンダー モードを設定する

次の例では、すべての要求に対して対話型サーバー側レンダリング (対話型 SSR) を適用しています。

<Routes @rendermode="RenderModeForPage" />

...

@code {
    private IComponentRenderMode? RenderModeForPage => InteractiveServer;
}

レンダリング モードの伝達について詳しくは、この記事の後半の「レンダー モードの伝達」セクションをご覧ください。 「レンダー モードの細かい制御」セクションでは、上記の方法を使用して、アプリの特定の領域 (フォルダー) またはコンポーネントごとのレンダー モードの割り当てのあるアプリの周囲の特定のコンポーネントに静的 SSR を採用する方法を示します。

プリレンダリング

プリレンダリングは、レンダリングされたコントロールのイベント ハンドラーを有効にせずに、最初にサーバーにページ コンテンツをレンダリングするプロセスです。 サーバーは、最初の要求に応じてできるだけ早くページの HTML UI を出力します。これにより、アプリはユーザーに対してより応答性が高くなります。 プリレンダリングでは、検索エンジンによってページ ランクの計算に使用される初期 HTTP 応答のコンテンツをレンダリングすることで、検索エンジンの最適化 (SEO) を向上させることもできます。

対話型コンポーネントではプリレンダリングが既定で有効になっています。

"コンポーネント インスタンス" のプリレンダリングを無効にするには、値が falseprerender フラグを、レンダリング モードに渡します。

  • <... @rendermode="new InteractiveServerRenderMode(prerender: false)" />
  • <... @rendermode="new InteractiveWebAssemblyRenderMode(prerender: false)" />
  • <... @rendermode="new InteractiveAutoRenderMode(prerender: false)" />

"コンポーネント定義" でプリレンダリングを無効にするには:

  • @rendermode @(new InteractiveServerRenderMode(prerender: false))
  • @rendermode @(new InteractiveWebAssemblyRenderMode(prerender: false))
  • @rendermode @(new InteractiveAutoRenderMode(prerender: false))

アプリ全体でプリレンダリングを無効にするには、アプリのコンポーネント階層の、ルート コンポーネントではない最上位の対話型コンポーネントで、レンダリング モードを指定します。

Blazor Web アプリ プロジェクト テンプレートに基づくアプリの場合、アプリ全体に割り当てられるレンダリング モードは、App コンポーネント (Components/App.razor) で Routes コンポーネントが使われる場所で指定されます。 次の例では、プリレンダリングを無効にして、アプリのレンダリング モードを対話型サーバーに設定しています。

<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />

また、App コンポーネントの HeadOutletコンポーネント のプリレンダリングを無効にします:

<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />

ルート コンポーネント (App コンポーネントなど) をルート コンポーネントの定義ファイル (.razor) の先頭にある @rendermode ディレクティブと対話形式にすることはサポートされていません。 そのため、プリレンダリングを App コンポーネントで直接無効にすることはできません。

静的サーバー側レンダリング (静的 SSR)

既定では、コンポーネントは静的サーバー側レンダリング (静的 SSR) を使用します。 コンポーネントが応答ストリームにレンダリングされ、インタラクティビティは有効になっていません。

次の例では、コンポーネントのレンダリング モードの指定はなく、コンポーネントはその親からレンダリング モードを継承します。 先祖コンポーネントがレンダリング モードを指定していないため、次のコンポーネントはサーバー上で静的にレンダリングされます。 ボタンはインタラクティブではなく、選択された場合に UpdateMessage メソッドを呼び出しません。 message の値は変更されず、コンポーネントは UI イベントに応答して再レンダリングされません。

RenderMode1.razor:

@page "/render-mode-1"

<button @onclick="UpdateMessage">Click me</button> @message

@code {
    private string message = "Not updated yet.";

    private void UpdateMessage()
    {
        message = "Somebody updated me!";
    }
}

上記のコンポーネントを Blazor Web アプリでローカルで使用する場合は、サーバー プロジェクトの Components/Pages フォルダーにコンポーネントを配置します。 サーバー プロジェクトはソリューションのプロジェクトであり、名前の末尾が .Client ではありません。 アプリが実行されている場合は、ブラウザーのアドレス バーで /render-mode-1 に移動します。

静的 SSR である間、Razor コンポーネント ページ要求は、サーバー側の ASP.NET Core ミドルウェア パイプライン要求の処理によって、ルーティングと承認のために処理されます。 Razor コンポーネントはサーバー側の要求の処理中にレンダリングされないため、ルーティングと承認のための専用の Blazor 機能は動作しません。 静的 SSR である間に使用できない Routes コンポーネントの Blazor ルーター機能には、次のような表示が含まれます。

アプリがルートレベルの対話機能を示す場合、サーバー側の ASP.NET Core 要求の処理は最初の静的 SSR の後に関与しません。つまり、上記の Blazor 機能は期待どおりに動作します。

静的 SSR による拡張ナビゲーションでは、JavaScript を読み込むときに特別な注意が必要です。 詳細については、「静的サーバー側レンダリング (静的 SSR) を使用した ASP.NET Core Blazor JavaScript」を参照してください。

対話型サーバー側レンダリング (対話型 SSR)

対話型サーバー側レンダリング (対話型 SSR) では、Blazor Server を使用して、サーバーからコンポーネントが対話形式でレンダリングされます。 ユーザー操作は、ブラウザーとのリアルタイム接続を介して処理されます。 サーバー コンポーネントがレンダリングされると、回線接続が確立されます。

次の例では、コンポーネント定義に @rendermode InteractiveServer を追加することで、レンダリング モードが対話型 SSR に設定されています。 このボタンを選択すると、UpdateMessage メソッドが呼び出されます。 message の値が変更され、コンポーネントが再レンダリングされて UI 内のメッセージが更新されます。

RenderMode2.razor:

@page "/render-mode-2"
@rendermode InteractiveServer

<button @onclick="UpdateMessage">Click me</button> @message

@code {
    private string message = "Not updated yet.";

    private void UpdateMessage()
    {
        message = "Somebody updated me!";
    }
}

上記のコンポーネントを Blazor Web アプリでローカルで使用する場合は、サーバー プロジェクトの Components/Pages フォルダーにコンポーネントを配置します。 サーバー プロジェクトはソリューションのプロジェクトであり、名前の末尾が .Client ではありません。 アプリが実行されている場合は、ブラウザーのアドレス バーで /render-mode-2 に移動します。

クライアント側レンダリング (CSR)

クライアント側レンダリング (CSR) では、Blazor WebAssembly を使用してコンポーネントがクライアント上で対話形式でレンダリングされます。 WebAssembly コンポーネントが最初にレンダリングされるときに、.NET ランタイムとアプリ バンドルがダウンロードされ、キャッシュされます。 CSR を使用するコンポーネントは、Blazor WebAssembly ホストを設定する別のクライアント プロジェクトからビルドする必要があります。

次の例では、@rendermode InteractiveWebAssembly を使用してレンダリング モードが CSR に設定されています。 このボタンを選択すると、UpdateMessage メソッドが呼び出されます。 message の値が変更され、コンポーネントが再レンダリングされて UI 内のメッセージが更新されます。

RenderMode3.razor:

@page "/render-mode-3"
@rendermode InteractiveWebAssembly

<button @onclick="UpdateMessage">Click me</button> @message

@code {
    private string message = "Not updated yet.";

    private void UpdateMessage()
    {
        message = "Somebody updated me!";
    }
}

上記のコンポーネントを Blazor Web アプリでローカルで使用する場合は、クライアント プロジェクトの Pages フォルダーにコンポーネントを配置します。 クライアント プロジェクトはソリューションのプロジェクトであり、名前の末尾が .Client です。 アプリが実行されている場合は、ブラウザーのアドレス バーで /render-mode-3 に移動します。

自動 (Auto) レンダリング

自動 (Auto) レンダリングでは、実行時にコンポーネントをレンダリングする方法が決まります。 コンポーネントは、最初は、Blazor Server ホスティング モデルを使用して、対話型サーバー側レンダリング (対話型 SSR) でレンダリングされます。 .NET ランタイムとアプリ バンドルがバックグラウンドでクライアントにダウンロードされ、キャッシュされるため、以降のアクセスで使用できます。

自動レンダリング モードでは、ページに既に存在するコンポーネントのレンダリング モードが動的に変更されることはありません。 自動レンダリング モードは、コンポーネントでどの種類のインタラクティビティが使われるかを最初に決定して、その後、コンポーネントがページ上にある限りその種類のインタラクティビティが維持されます。 この最初の決定における 1 つの要素は、WebAssembly/サーバーのインタラクティビティを備えたコンポーネントがページ上に既に存在するかどうかを考慮することです。 自動モードでは、既存の対話型コンポーネントのレンダリング モードと一致するレンダリング モードが優先的に選ばれます。 自動モードが既存のインタラクティビティ モードの使用を優先する理由は、既存のランタイムと状態を共有しない新しい対話型ランタイムの導入を避けるためです。

自動レンダー モードを使用するコンポーネントは、Blazor WebAssembly ホストを設定する別のクライアント プロジェクトからビルドする必要があります。

次の例では、コンポーネントはプロセス全体を通してインタラクティブです。 このボタンを選択すると、UpdateMessage メソッドが呼び出されます。 message の値が変更され、コンポーネントが再レンダリングされて UI 内のメッセージが更新されます。 最初は、コンポーネントはサーバーからインタラクティブにレンダリングされますが、その後のアクセスでは、.NET ランタイムとアプリ バンドルがダウンロードされてキャッシュされた後にクライアントからレンダリングされます。

RenderMode4.razor:

@page "/render-mode-4"
@rendermode InteractiveAuto

<button @onclick="UpdateMessage">Click me</button> @message

@code {
    private string message = "Not updated yet.";

    private void UpdateMessage()
    {
        message = "Somebody updated me!";
    }
}

上記のコンポーネントを Blazor Web アプリでローカルで使用する場合は、クライアント プロジェクトの Pages フォルダーにコンポーネントを配置します。 クライアント プロジェクトはソリューションのプロジェクトであり、名前の末尾が .Client です。 アプリが実行されている場合は、ブラウザーのアドレス バーで /render-mode-4 に移動します。

レンダー モードの伝達

レンダー モードは、コンポーネント階層の下に伝達されます。

レンダー モードを適用するためのルール:

  • 既定のレンダー モードは静的です。
  • 対話型サーバー、(InteractiveServer)、対話型 WebAssembly (InteractiveWebAssembly)、対話型自動 (InteractiveAuto) の各レンダリング モードは、コンポーネントから使用でき、兄弟コンポーネントに異なるレンダリング モードを使用することもできます。
  • 子コンポーネントで別のインタラクティブ レンダー モードに切り替えることはできません。 たとえば、サーバー コンポーネントを WebAssembly コンポーネントの子にすることはできません。
  • 静的な親からインタラクティブな子コンポーネントに渡されるパラメーターは、JSON シリアル化可能である必要があります。 つまり、静的な親コンポーネントからインタラクティブな子コンポーネントにレンダー フラグメントや子コンテンツを渡すことはできません。

次の例では、ルーティング不可能な非ページ SharedMessage コンポーネントを使用しています。 レンダー モードに依存しない SharedMessage コンポーネントでは、@attribute ディレクティブによるレンダー モードは適用されません。 Blazor Web アプリでこれらのシナリオをテストする場合は、アプリの Components フォルダーに次のコンポーネントを配置します。

SharedMessage.razor:

<p>@Greeting</p>

<button @onclick="UpdateMessage">Click me</button> @message

<p>@ChildContent</p>

@code {
    private string message = "Not updated yet.";

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    [Parameter]
    public string Greeting { get; set; } = "Hello!";

    private void UpdateMessage()
    {
        message = "Somebody updated me!";
    }
}

レンダー モードの継承

SharedMessage コンポーネントが静的にレンダリングされる親コンポーネントに配置されている場合、SharedMessage コンポーネントも静的にレンダリングされ、インタラクティブではありません。 ボタンは UpdateMessage を呼び出さず、メッセージは更新されません。

RenderMode5.razor:

@page "/render-mode-5"

<SharedMessage />

SharedMessage コンポーネントがレンダー モードを定義するコンポーネントに配置されている場合、適用されたレンダー モードが継承されます。

次の例では、SharedMessage コンポーネントはクライアントへの SignalR 接続でインタラクティブです。 ボタンが UpdateMessage を呼び出し、メッセージが更新されます。

RenderMode6.razor:

@page "/render-mode-6"
@rendermode InteractiveServer

<SharedMessage />

レンダー モードが異なる子コンポーネント

次の例では、SharedMessage コンポーネントの両方が (既定で) プリレンダリングされ、ページがブラウザーに表示されるときに表示されます。

  • 対話型サーバー側レンダリング (対話型 SSR) を使用する最初の SharedMessage コンポーネントは、SignalR 回線が確立された後に対話型になります。
  • クライアント側レンダリング (CSR) を使用する 2 つ目の SharedMessage コンポーネントは、Blazor アプリ バンドルがダウンロードされ、.NET ランタイムがクライアントでアクティブになった "後" に対話型になります。

RenderMode7.razor:

@page "/render-mode-7"

<SharedMessage @rendermode="InteractiveServer" />
<SharedMessage @rendermode="InteractiveWebAssembly" />

シリアル化可能なパラメーターを持つ子コンポーネント

次の例では、パラメーターを受け取るインタラクティブな子コンポーネントを示しています。 パラメーターはシリアル化可能である必要があります。

RenderMode8.razor:

@page "/render-mode-8"

<SharedMessage @rendermode="InteractiveServer" Greeting="Welcome!" />

子コンテンツやレンダー フラグメントなどのシリアル化不可能なコンポーネント パラメーターは、サポート "されません"。 次の例では、子コンテンツを SharedMessage コンポーネントに渡すことで、ランタイム エラーが発生します。

RenderMode9.razor:

@page "/render-mode-9"

<SharedMessage @rendermode="InteractiveServer">
    Child content
</SharedMessage>

エラー:

System.InvalidOperationException: rendermode 'InteractiveServerRenderMode' を使って、パラメーター 'ChildContent' をコンポーネント 'SharedMessage' に渡すことはできません。 これは、パラメーターがデリゲート型 'Microsoft.AspNetCore.Components.RenderFragment' であるためです。これは任意のコードであり、シリアル化できません。

上記の制限を回避するには、パラメーターを持たない別のコンポーネントで子コンポーネントをラップします。 これは、Router コンポーネントをラップするために Routes コンポーネント (Components/Routes.razor) を使用する Blazor Web アプリ プロジェクト テンプレートで利用されるアプローチです。

WrapperComponent.razor:

<SharedMessage>
    Child content
</SharedMessage>

RenderMode10.razor:

@page "/render-mode-10"

<WrapperComponent @rendermode="InteractiveServer" />

前の例の場合:

  • 子コンテンツは SharedMessage コンポーネントに渡され、ランタイム エラーは生成されません。
  • SharedMessage コンポーネントは、サーバー上でインタラクティブにレンダリングされます。

親と異なるレンダー モードを持つ子コンポーネント

子コンポーネントに対して、親のレンダー モードとは異なるインタラクティブ レンダー モードを適用しないでください。

次のコンポーネントでは、コンポーネントのレンダリング時にランタイム エラーが発生します。

RenderMode11.razor:

@page "/render-mode-11"
@rendermode InteractiveServer

<SharedMessage @rendermode="InteractiveWebAssembly" />

エラー:

Cannot create a component of type 'BlazorSample.Components.SharedMessage' because its render mode 'Microsoft.AspNetCore.Components.Web.InteractiveWebAssemblyRenderMode' is not supported by Interactive Server rendering.

レンダー モードの細かい制御

アプリの仕様により、静的サーバー側レンダリング (静的 SSR) を採用し、サーバー上でのみ実行することが求められるが、アプリの残りの部分では対話型レンダー モードが使用される場合があります。

レンダー モードを細かく制御するには 2 つの方法があります。それぞれについて、次のサブセクションで説明します。

  • 静的 SSR コンポーネントの領域 (フォルダー): 静的 SSR を採用し、同じルート パス プレフィックスを共有する必要があるコンポーネントを含むアプリの領域 (フォルダー) があります。 アプリは、フォルダーへのパスに基づいて App コンポーネント内の Routes コンポーネントにレンダー モードを設定することで、レンダー モードをグローバルに制御します。

  • アプリ全体に分散した静的 SSR コンポーネント: 静的 SSR を採用し、サーバー上でのみ実行する必要があるコンポーネントが、アプリの周囲のさまざまな場所に分散しています。 静的 SSR 専用のコンポーネントは 1 つのフォルダーに含まれておらず、共通のルート パス プレフィックスを共有していません。 アプリは、コンポーネント インスタンスの @rendermode ディレクティブを使用してレンダー モードを設定することで、コンポーネントごとにレンダー モードを制御します。 App コンポーネントでリフレクションを使用して、Routes コンポーネントのレンダー モードを設定します。

どちらの場合も、静的 SSR を採用する必要があるコンポーネントは、ページ全体の再読み込みも強制する必要があります。

次の例では、HttpContext カスケード パラメーターを使用して、ページが静的にレンダリングされているかどうかを判断します。 nullHttpContext は、コンポーネントが対話形式でレンダリングされていることを示します。これは、ページ全体の再読み込みをトリガーするアプリ コードのシグナルとして役立ちます。

静的 SSR コンポーネントの領域 (フォルダー)

このサブセクションで説明する方法は、個々の認証とグローバル対話機能を使用して Blazor Web アプリ プロジェクト テンプレートによって使用されます。

アプリの領域 (フォルダー) には、静的 SSR を採用し、サーバーでのみ実行する必要があるコンポーネントが含まれています。 フォルダー内のコンポーネントは、同じルート パス プレフィックスを共有します。 たとえば、Blazor Web アプリ プロジェクト テンプレートの IdentityRazor コンポーネントはComponents/Account/Pages フォルダー内にあり、ルート パス プレフィックス /Account を共有します。

このフォルダーには、フォルダー内のコンポーネントにカスタム アカウント レイアウトを適用する _Imports.razor ファイルも含まれています。

@using BlazorSample.Components.Account.Shared
@layout AccountLayout

Shared フォルダーには AccountLayout レイアウト コンポーネントが保持されています。 このコンポーネントは、HttpContext を使用して、コンポーネントがサーバー上でレンダリングされているかどうかを判断します。 Identity コンポーネントは Identitycookie を設定しているため、静的 SSR を使用してサーバー上にレンダリングする必要があります。 HttpContext の値が null の場合、コンポーネントは対話形式でレンダリングされ、forceLoadtrue に設定して NavigationManager.Refresh を呼び出すことによってページ全体の再読み込みが実行されます。 これにより、静的 SSR を使用してページの完全な再レンダリングが強制されます。

Components/Account/Shared/AccountLayout.razor:

@inherits LayoutComponentBase
@layout BlazorSample.Components.Layout.MainLayout
@inject NavigationManager NavigationManager

@if (HttpContext is null)
{
    <p>Loading...</p>
}
else
{
    @Body
}

@code {
    [CascadingParameter]
    private HttpContext? HttpContext { get; set; }

    protected override void OnParametersSet()
    {
        if (HttpContext is null)
        {
            NavigationManager.Refresh(forceReload: true);
        }
    }
}

Note

Blazor Web アプリ プロジェクト テンプレートには、Components/Account/Pages/Manage フォルダー内の Identity コンポーネント用の 2 つ目のレイアウト ファイル (Components/Account/Shared フォルダー内の ManageLayout.razor) があります。 Manage フォルダーには、フォルダー内の ManageLayout コンポーネントに適用する独自の _Imports.razor ファイルがあります。 独自のアプリで、入れ子になった _Imports.razor ファイルを使用すると、ページのグループにカスタム レイアウトを適用する際に便利です。

App コンポーネントでは、Account フォルダー内のコンポーネントに対するすべての要求で null レンダー モードが適用され、静的 SSR が強制されます。 その他のコンポーネント要求は、対話型 SSR レンダー モード (InteractiveServer) のグローバル アプリケーションを受け取ります。

重要

null レンダー モードを適用しても、必ずしも静的 SSR が強制されるわけではありません。 このセクションに示す方法を使用して、そのように動作しているだけです。

null レンダー モードは、レンダリング モードを指定しない場合と実質的に同じであり、その結果、コンポーネントは親のレンダー モードを継承します。 この場合、App コンポーネントは静的 SSR を使用してレンダリングされるため、null レンダー モードの結果、Routes コンポーネントは App コンポーネントから 静的 SSR を継承します。 親が対話型レンダー モードを使用する子コンポーネントに対して null レンダー モードが指定されている場合、子は同じ対話型レンダリング モードを継承します。

Components/App.razor:

<Routes @rendermode="RenderModeForPage" />

...

@code {
    [CascadingParameter]
    private HttpContext HttpContext { get; set; } = default!;

    private IComponentRenderMode? RenderModeForPage => 
        HttpContext.Request.Path.StartsWithSegments("/Account")
            ? null
            : {INTERACTIVE RENDER MODE};
}

前のコードでは、アプリケーションの残りの部分でグローバル InteractiveServerInteractiveWebAssembly、または InteractiveAuto レンダリングを採用する必要があるかどうかに応じて、{INTERACTIVE RENDER MODE} プレースホルダーを適切な値に変更します。

Account フォルダー内の静的 SSR を採用する必要があるコンポーネントは、レイアウトを設定する必要はありません。なぜなら、レイアウトは _Imports.razor ファイルを介して適用されるためです。また、静的 SSR でレンダリングする必要があるため、コンポーネントはレンダリング モードを設定 "しません"。 Account フォルダー内のコンポーネントで静的 SSR を適用するために、これ以上何も行う必要はありません。

アプリ全体に分散している静的 SSR コンポーネント

前のサブセクションでは、アプリが App コンポーネントでレンダー モードをグローバルに設定することで、コンポーネントのレンダー モードを制御していました。 また、App コンポーネントでは、レンダー モードを設定するために "コンポーネントごと" のレンダー モードを採用することもできます。これにより、コンポーネントをアプリの周囲に分散して静的 SSR の採用を強制できます。 このサブセクションでは、このアプローチについて説明します。

アプリには、アプリの周囲のコンポーネントに適用できるカスタム レイアウトがあります。 通常、アプリの共有コンポーネントは Components/Layout フォルダーに配置されます。 このコンポーネントは、HttpContext を使用して、コンポーネントがサーバー上でレンダリングされているかどうかを判断します。 HttpContext の値が null の場合、コンポーネントは対話形式でレンダリングされ、forceLoadtrue に設定して NavigationManager.Refresh を呼び出すことによってページ全体の再読み込みが実行されます。 これにより、コンポーネントについてのサーバーへの要求がトリガーされます。

Components/Layout/StaticSsrLayout.razor:

@inherits LayoutComponentBase
@layout MainLayout
@inject NavigationManager NavigationManager

@if (HttpContext is null)
{
    <p>Loading...</p>
}
else
{
    @Body
}

@code {
    [CascadingParameter]
    private HttpContext? HttpContext { get; set; }

    protected override void OnParametersSet()
    {
        if (HttpContext is null)
        {
            NavigationManager.Refresh(forceReload: true);
        }
    }
}

App コンポーネントでは、リフレクションを使用してレンダー モードを設定します。 個々のコンポーネント定義ファイルに割り当てられているレンダリング モードが、Routes コンポーネントに適用されます。

Components/App.razor:

<Routes @rendermode="RenderModeForPage" />

...

@code {
    [CascadingParameter]
    private HttpContext HttpContext { get; set; } = default!;

    private IComponentRenderMode? RenderModeForPage =>
        HttpContext.GetEndpoint()?.Metadata.GetMetadata<RenderModeAttribute>()?
            .Mode;
}

静的 SSR を採用する必要がある各コンポーネントは、カスタム レイアウトを設定し、レンダー モードを指定 "しません"。 レンダー モードを指定しないと、App コンポーネントの RenderModeAttribute.Mode 値が null となり、その結果、Routes コンポーネント インスタンスにレンダー モードが割り当てられず、静的 SSR が適用されます。

重要

null レンダー モードを適用しても、必ずしも静的 SSR が強制されるわけではありません。 このセクションに示す方法を使用して、そのように動作しているだけです。

null レンダー モードは、レンダリング モードを指定しない場合と実質的に同じであり、その結果、コンポーネントは親のレンダー モードを継承します。 この場合、App コンポーネントは静的 SSR を使用してレンダリングされるため、null レンダー モードの結果、Routes コンポーネントは App コンポーネントから 静的 SSR を継承します。 親が対話型レンダー モードを使用する子コンポーネントに対して null レンダー モードが指定されている場合、子は同じ対話型レンダリング モードを継承します。

静的 SSR を適用するために、対話型レンダリング モードを設定せずにカスタム レイアウトを適用する以外にコンポーネントに対して何も行う必要はありません。

@layout BlazorSample.Components.Layout.StaticSsrLayout

アプリの周囲の対話型コンポーネントは、カスタム静的 SSR レイアウトの適用を回避しApp コンポーネント内のリフレクション時に Routes コンポーネントに適用される適切な対話型レンダリング モードのみを設定します

@rendermode {INTERACTIVE RENDER MODE}

前のコードでは、コンポーネントで InteractiveServerInteractiveWebAssembly、または InteractiveAuto レンダリングを採用する必要があるかどうかに応じて、{INTERACTIVE RENDER MODE} プレースホルダーを適切な値に変更します。

プリレンダリング中にクライアント側サービスによる解決が失敗する

コンポーネントまたはアプリでプリレンダリングが無効になっていないとすると、.Client プロジェクト内のコンポーネントはサーバー上でプリレンダリングされます。 サーバーは登録されたクライアント側 Blazor サービスにアクセスできないため、これらのサービスをコンポーネントに挿入することはできず、プリレンダリング中にサービスが見つからないというエラーを受け取ります。

たとえば、グローバルな対話型 WebAssembly または対話型自動レンダリングを使用する、Blazor Web アプリの .Client プロジェクト内の次のような Home コンポーネントについて考えます。 このコンポーネントは、環境の名前を取得するために IWebAssemblyHostEnvironment を挿入しようとします。

@page "/"
@inject IWebAssemblyHostEnvironment Environment

<PageTitle>Home</PageTitle>

<h1>Home</h1>

<p>
    Environment: @Environment.Environment
</p>

コンパイル時エラーは発生しませんが、プリレンダリング中に実行時エラーが発生します。

Cannot provide a value for property 'Environment' on type 'BlazorSample.Client.Pages.Home'. (型 'BlazorSample.Client.Pages.Home' のプロパティ 'Environment' の値を指定できません)。 There is no registered service of type 'Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment'. ('Microsoft.AspNetCore.Components.WebAssembly.Hosting.IWebAssemblyHostEnvironment' 型の登録されたサービスはありません。)

このエラーは、コンポーネントはプリレンダリング中にサーバーでコンパイルおよび実行される必要がありますが、IWebAssemblyHostEnvironment はサーバー上の登録されたサービスでないために発生します。

プリレンダリング中にアプリで値が必要ない場合は、サービスの種類自体ではなく、IServiceProvider を挿入してサービスを取得することにより、この問題を解決できます。

@page "/"
@using Microsoft.AspNetCore.Components.WebAssembly.Hosting
@inject IServiceProvider Services

<PageTitle>Home</PageTitle>

<h1>Home</h1>

<p>
    <b>Environment:</b> @environmentName
</p>

@code {
    private string? environmentName;

    protected override void OnInitialized()
    {
        if (Services.GetService<IWebAssemblyHostEnvironment>() is { } env)
        {
            environmentName = env.Environment;
        }
    }
}

ただし、上の方法は、プリレンダリング中にロジックで値が必要な場合は役に立ちません。

また、コンポーネントのプリレンダリングを無効にした場合も問題を回避できますが、多くの場合はコンポーネントの仕様を満たしていない可能性があるときに取るべき非常手段です。

このシナリオに対処するために取ることができる 3 つの方法があります。 以下では、最も推奨されるものから順に示します。

  • 共有フレームワークのサービスで "推奨": 共有フレームワークのサービスがメイン プロジェクトのサーバー側に登録されていないだけの場合には、メイン プロジェクトにそのサービスを登録します。これにより、プリレンダリング中にサービスを利用できるようになります。 このシナリオの例については、「ASP.NET Core Blazor アプリから Web API を呼び出す」の HttpClient サービスのガイダンスを参照してください。

  • 共有フレームワークに含まれないサービスで "推奨": サーバー上のサービスに対するカスタム サービス実装を作成します。 通常は、.Client プロジェクトの対話型コンポーネントでそのサービスを使います。 この方法のデモについては、「ASP.NET Core Blazor の環境」をご覧ください。

  • サービスの抽象化を作成し、.Client とサーバーのプロジェクトでサービスの実装を作成します。 各プロジェクトでサービスを登録します。 カスタム サービスをコンポーネントに挿入します。

  • .Client プロジェクト パッケージの参照をサーバー側パッケージに追加し、サーバーでのプリレンダリング時にはサーバー側 API の使用にフォールバックできる場合があります。

追加アセンブリからコンポーネントを検出する

参照先のプロジェクトでルーティング可能な Razor コンポーネントを検出するには、追加のアセンブリを Blazor フレームワークに公開する必要があります。 詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。

対話型サーバー コンポーネントが残っていない場合に回線を閉じる

対話型サーバー コンポーネントでは、ブラウザーとのリアルタイム接続 (回線) を使用して、Web UI イベントを処理します。 ルートの対話型サーバー コンポーネントがレンダリングされる際、回線およびその関連付けられた状態が作成されます。 ページ上に対話型サーバー コンポーネントが残っていない場合、回線は閉じられ、サーバー リソースは解放されます。

カスタムの短縮形レンダリング モード

@rendermode ディレクティブは、IComponentRenderMode 型の静的なインスタンスである 1 つのパラメーターを受け取ります。 @rendermode ディレクティブ属性は、静的かどうかに関係なく、任意のレンダリング モード インスタンスを受け取ることができます。 Blazor フレームワークには、便利な事前定義されたレンダリング モードをいくつか備えた RenderMode 静的クラスが用意されています、独自に作成することもできます。

通常、コンポーネントは次の @rendermode ディレクティブを使ってプリレンダリングを無効にします。

@rendermode @(new InteractiveServerRenderMode(prerender: false))

しかし、プリレンダリングを行わずに、アプリの _Imports ファイル (Components/_Imports.razor) を使って短縮形の対話型サーバー レンダリング モードを作成する次の例を考えてみます。

public static IComponentRenderMode InteractiveServerWithoutPrerendering { get; } = 
    new InteractiveServerRenderMode(prerender: false);

Components フォルダー全体のコンポーネントで、短縮形のレンダリング モードを使います。

@rendermode InteractiveServerWithoutPrerendering

または、1 つのコンポーネント インスタンスで、プライベート フィールドを使ってカスタム レンダリング モードを定義することもできます。

@rendermode interactiveServerWithoutPrerendering

...

@code {
    private static IComponentRenderMode interactiveServerWithoutPrerendering = 
        new InteractiveServerRenderMode(prerender: false);
}

現時点で、短縮形レンダリング モードのアプローチは、おそらく prerender フラグの詳細な指定を軽減するためにのみ役立ちます。 将来的に、対話型レンダリングで使用できるフラグが追加され、さまざまなフラグの組み合わせで短縮形レンダリング モードを作成できるようになれば、短縮形のアプローチの方が便利になる可能性があります。

最上位レベルのインポート ファイル (_Imports.razor) を使用したサービスの挿入

"このセクションは Blazor Web アプリにのみ適用されます。"

Components フォルダーにある最上位レベルのインポート ファイル (Components/_Imports.razor) によって、その参照がフォルダー階層内のすべてのコンポーネントに挿入されます。これには App コンポーネント (App.razor) が含まれます。 ページ コンポーネントのプリレンダリングが無効になっている場合でも、App コンポーネントは常に静的にレンダリングされます。 したがって、最上位レベルのインポート ファイルを介してサービスを挿入すると、ページ コンポーネントにおいてサービスの "2 つのインスタンス" が解決されます。

このシナリオに対処するには、Pages フォルダーに配置した新しいインポート ファイル (Components/Pages/_Imports.razor) でサービスを挿入します。 その場所から、サービスはページ コンポーネントにおいて 1 回だけ解決されます。

その他のリソース