注記
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
重要
この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。
現在のリリースについては、この記事の .NET 9 バージョンを参照してください。
この記事では、Razor でのサーバーでレンダリングされるコンポーネントの Blazor Web App コンポーネント プリレンダリング シナリオについて説明します。
プリレンダリングは、レンダリングされたコントロールのイベント ハンドラーを有効にせずに、最初にサーバーにページ コンテンツをレンダリングするプロセスです。 サーバーは、最初の要求に応じてできるだけ早くページの HTML UI を出力します。これにより、アプリはユーザーに対してより応答性が高くなります。 プリレンダリングでは、検索エンジンによってページ ランクの計算に使用される初期 HTTP 応答のコンテンツをレンダリングすることで、検索エンジンの最適化 (SEO) を向上させることもできます。
プリレンダリングされた状態を保持する
プリレンダリングされた状態を保持しないと、プリレンダリング中に使用された状態は失われ、アプリが完全に読み込まれたときに再作成する必要があります。 状態が非同期的に作成される場合、コンポーネントの再レンダリングによってプリレンダリングされたUIが置き換えられる際に、UIが一時的にちらつくことがあります。
次の PrerenderedCounter1
カウンター コンポーネントについて検討します。 コンポーネントは、OnInitialized
ライフサイクル メソッドでのプリレンダリング中に初期ランダム カウンター値を設定します。 クライアントへの SignalR 接続が確立されると、コンポーネントが再レンダリングされ、OnInitialized
の 2 回目の実行時に初期カウント値が置き換えられます。
PrerenderedCounter1.razor
:
@page "/prerendered-counter-1"
@rendermode @(new InteractiveServerRenderMode(prerender: true))
@inject ILogger<PrerenderedCounter1> Logger
<PageTitle>Prerendered Counter 1</PageTitle>
<h1>Prerendered Counter 1</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount;
protected override void OnInitialized()
{
currentCount = Random.Shared.Next(100);
Logger.LogInformation("currentCount set to {Count}", currentCount);
}
private void IncrementCount() => currentCount++;
}
アプリを実行し、コンポーネントのログを調査します。 以下に、出力の例を示します。
注記
アプリが対話型ルーティング PrerenderedCounter1
コンポーネントのページ全体の再読み込みを行う必要があります。 詳細については、「対話型ルーティングとプリレンダリングの」セクションを参照してください。
info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 41
info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 92
最初のログ カウントはプリレンダリング中に生じます。 コンポーネントが再レンダリングされると、プリレンダリング後にカウントが再び設定されます。 カウントが 41 から 92 に更新されるときにも、UI にちらつきが生じます。
プリレンダリング中にカウンターの初期値を保持するために、Blazor では、PersistentComponentState サービス (さらに、Razor Pages または MVC アプリのページまたはビューに埋め込まれたコンポーネントの場合は、コンポーネントの状態保持タグ ヘルパー) を使用してプリレンダリングされたページ内の状態の永続化をサポートしています。
プリレンダリングされた状態を保持するには、 [SupplyParameterFromPersistentComponentState]
属性を使用してプロパティの状態を保持します。 この属性を持つプロパティは、プリレンダリング中に PersistentComponentState サービスを使用して自動的に永続化されます。 状態は、コンポーネントが対話形式でレンダリングされるか、サービスがインスタンス化されるときに取得されます。
既定では、プロパティは既定の設定で System.Text.Json シリアライザーを使用してシリアル化されます。 シリアル化はトリマー使用に対する安全性がなく、使用される型の保存が必要です。 詳しくは、「ASP.NET Core Blazor 用のトリマーを構成する」をご覧ください。
次のカウンター コンポーネントは、プリレンダリング中にカウンター状態を保持し、コンポーネントを初期化する状態を取得します。
-
[SupplyParameterFromPersistentComponentState]
属性は、CounterState
型 (State
) に適用されます。 - カウンターの状態は、
null
でOnInitialized
する際に割り当てられ、コンポーネントがインタラクティブにレンダリングされる際に自動的に復元されます。
PrerenderedCounter2.razor
:
@page "/prerendered-counter-2"
@inject ILogger<PrerenderedCounter2> Logger
<PageTitle>Prerendered Counter 2</PageTitle>
<h1>Prerendered Counter 2</h1>
<p role="status">Current count: @State?.CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
[SupplyParameterFromPersistentComponentState]
public CounterState? State { get; set; }
protected override void OnInitialized()
{
if (State is null)
{
State = new() { CurrentCount = Random.Shared.Next(100) };
Logger.LogInformation("CurrentCount set to {Count}",
State.CurrentCount);
}
else
{
Logger.LogInformation("CurrentCount restored to {Count}",
State.CurrentCount);
}
}
private void IncrementCount()
{
if (State is not null)
{
State.CurrentCount++;
}
}
public class CounterState
{
public int CurrentCount { get; set; }
}
}
コンポーネントが実行されると、プリレンダリング中に CurrentCount
が 1 回だけ設定されます。 この値は、コンポーネントが再レンダリングされると復元されます。 以下に、出力の例を示します。
注記
アプリが対話型ルーティング
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount restored to 96
次の例では、同じ型の複数のコンポーネントの状態をシリアル化します。
-
[SupplyParameterFromPersistentComponentState]
属性で注釈が付けられたプロパティは、プリレンダリング中にシリアル化および逆シリアル化されます。 -
@key
ディレクティブ属性は、状態がコンポーネント インスタンスに正しく関連付けられていることを確認するために使用されます。 -
Element
プロパティは、クエリ パラメーターやフォーム データに対する null 参照の回避方法と同様に、null 参照の例外を回避するために、OnInitialized
ライフサイクル メソッドで初期化されます。
PersistentChild.razor
:
<div>
<p>Current count: @Element.CurrentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</div>
@code {
[SupplyParameterFromPersistentComponentState]
public State Element { get; set; }
protected override void OnInitialized()
{
Element ??= new State();
}
private void IncrementCount()
{
Element.CurrentCount++;
}
private class State
{
public int CurrentCount { get; set; }
}
}
Parent.razor
:
@page "/parent"
@foreach (var element in elements)
{
<PersistentChild @key="element.Name" />
}
依存関係挿入サービスの状態をシリアル化する次の例では、
-
[SupplyParameterFromPersistentComponentState]
属性で注釈が付けられたプロパティは、プリレンダリング中にシリアル化され、アプリが対話型になると逆シリアル化されます。 -
AddPersistentService
メソッドは、永続化のためにサービスを登録するために使用されます。 レンダリング モードはサービスの種類から推論できないため、レンダリング モードが必要です。 次のいずれかの値を使用します。-
RenderMode.Server
: サービスは、対話型サーバー レンダリング モードで使用できます。 -
RenderMode.Webassembly
: サービスは対話型 Webassembly レンダリング モードで使用できます。 -
RenderMode.InteractiveAuto
: コンポーネントがいずれかのモードでレンダリングされる場合、サービスは Interactive Server レンダリング モードと Interactive Webassembly レンダリング モードの両方で使用できます。
-
- サービスは対話型レンダリング モードの初期化中に解決され、
[SupplyParameterFromPersistentComponentState]
属性で注釈が付けられたプロパティは逆シリアル化されます。
注記
スコープ付きサービスの永続化のみがサポートされています。
CounterService.cs
:
public class CounterService
{
[SupplyParameterFromPersistentComponentState]
public int CurrentCount { get; set; }
public void IncrementCount()
{
CurrentCount++;
}
}
Program.cs
の場合:
builder.Services.AddPersistentService<CounterService>(RenderMode.InteractiveAuto);
シリアル化されたプロパティは、実際のサービス インスタンスから識別されます。
- この方法では、抽象化を永続的なサービスとしてマークできます。
- 実際の実装を内部型または異なる型にできます。
- さまざまなアセンブリの共有コードをサポートします。
- 結果として、各インスタンスで同じプロパティが公開されます。
[SupplyParameterFromPersistentComponentState]
属性を使用して状態を永続化するために宣言型モデルを使用する代わりに、PersistentComponentState サービスを直接使用することもできます。これによって、複雑な状態永続化シナリオの柔軟性が向上します。 プリレンダリング中にコンポーネントの状態を保持するコールバックを登録する PersistentComponentState.RegisterOnPersisting を呼び出します。 状態は、コンポーネントが対話形式でレンダリングされるときに取得されます。 アプリのシャットダウン中に競合状態になる可能性を回避するために、初期化コードの最後に呼び出しを行います。
次のカウンター コンポーネントでは、プリレンダリング中にカウンターの状態を保持し、コンポーネントを初期化するためにその状態を取得します。
PrerenderedCounter3.razor
:
@page "/prerendered-counter-3"
@implements IDisposable
@inject ILogger<PrerenderedCounter3> Logger
@inject PersistentComponentState ApplicationState
<PageTitle>Prerendered Counter 3</PageTitle>
<h1>Prerendered Counter 3</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount;
private PersistingComponentStateSubscription persistingSubscription;
protected override void OnInitialized()
{
if (!ApplicationState.TryTakeFromJson<int>(
nameof(currentCount), out var restoredCount))
{
currentCount = Random.Shared.Next(100);
Logger.LogInformation("currentCount set to {Count}", currentCount);
}
else
{
currentCount = restoredCount!;
Logger.LogInformation("currentCount restored to {Count}", currentCount);
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistCount);
}
private Task PersistCount()
{
ApplicationState.PersistAsJson(nameof(currentCount), currentCount);
return Task.CompletedTask;
}
private void IncrementCount() => currentCount++;
void IDisposable.Dispose() => persistingSubscription.Dispose();
}
コンポーネントが実行されると、プリレンダリング中に currentCount
が 1 回だけ設定されます。 この値は、コンポーネントが再レンダリングされると復元されます。 以下に、出力の例を示します。
注記
アプリが対話型ルーティング
info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount restored to 96
プリレンダリングされた状態を保持するには、PersistentComponentState サービスを使用して、どの状態を永続化するかを決定します。 PersistentComponentState.RegisterOnPersisting は、プリレンダリング中にコンポーネントの状態を保持するコールバックを登録します。 状態は、コンポーネントが対話形式でレンダリングされるときに取得されます。 アプリのシャットダウン中に競合状態になる可能性を回避するために、初期化コードの最後に呼び出しを行います。
次のカウンター コンポーネントでは、プリレンダリング中にカウンターの状態を保持し、コンポーネントを初期化するためにその状態を取得します。
PrerenderedCounter2.razor
:
@page "/prerendered-counter-2"
@implements IDisposable
@inject ILogger<PrerenderedCounter2> Logger
@inject PersistentComponentState ApplicationState
<PageTitle>Prerendered Counter 2</PageTitle>
<h1>Prerendered Counter 2</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount;
private PersistingComponentStateSubscription persistingSubscription;
protected override void OnInitialized()
{
if (!ApplicationState.TryTakeFromJson<int>(
nameof(currentCount), out var restoredCount))
{
currentCount = Random.Shared.Next(100);
Logger.LogInformation("currentCount set to {Count}", currentCount);
}
else
{
currentCount = restoredCount!;
Logger.LogInformation("currentCount restored to {Count}", currentCount);
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistCount);
}
private Task PersistCount()
{
ApplicationState.PersistAsJson(nameof(currentCount), currentCount);
return Task.CompletedTask;
}
void IDisposable.Dispose() => persistingSubscription.Dispose();
private void IncrementCount() => currentCount++;
}
コンポーネントが実行されると、プリレンダリング中に currentCount
が 1 回だけ設定されます。 この値は、コンポーネントが再レンダリングされると復元されます。 以下に、出力の例を示します。
注記
アプリが対話型ルーティング
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount restored to 96
プリレンダリング中に使用されたのと同じ状態でコンポーネントを初期化することにより、負荷の高い初期化ステップが 1 回だけ実行されます。 レンダリングされた UI もプリレンダリングされた UI に一致するので、ブラウザーでちらつきは発生しません。
永続化されたプリレンダリングされた状態はクライアントに転送され、そこでコンポーネントの状態を復元するために使用されます。 クライアント側のレンダリング (CSR、 InteractiveWebAssembly
) 中に、データはブラウザーに公開され、機密情報を含めてはなりません。 対話型のサーバー側レンダリング (対話型 SSR、 InteractiveServer
) 中に、コア データ保護 ASP.NET データが安全に転送されるようにします。
InteractiveAuto
レンダリング モードは WebAssembly とサーバーの対話機能を組み合わせたものなので、CSR の場合と同様に、ブラウザーへのデータ公開を考慮する必要があります。
ページとビューに埋め込まれたコンポーネント (Razor Pages/MVC)
Razor Pages または MVC アプリのページまたはビューに埋め込まれたコンポーネントに対しては、アプリのレイアウトの終了 タグ内に、<persist-component-state />
HTML タグを含む</body>
を追加する必要があります。 これは、Razor Pages アプリと MVC アプリでのみ必要です。 詳細については、「ASP.NET Core でのコンポーネントの状態保持タグ ヘルパー」を参照してください。
Pages/Shared/_Layout.cshtml
:
<body>
...
<persist-component-state />
</body>
対話型ルーティングとプリレンダリング
Routes
コンポーネントでレンダリング モードが定義されていない場合、アプリはページ単位/コンポーネントごとの対話機能とナビゲーションを使用します。 ページ単位またはコンポーネント単位のナビゲーションを使用すると、内部†ナビゲーションは、アプリが対話型になった後 拡張ルーティング によって処理されます。 †この文脈での内部 とは、ナビゲーション イベントの URL の宛先がアプリ内の Blazor エンドポイントであることを意味します。
PersistentComponentState サービスは、初期ページ読み込みでのみ機能し、内部拡張ページ ナビゲーション イベントでは機能しません。
永続的なコンポーネントの状態を利用するページへの完全な (拡張されていない) ナビゲーションをアプリが実行すると、永続化された状態が対話型になったときにアプリで使用できるようになります。
対話型回線が既に確立されていて、永続的なコンポーネントの状態を利用するページへの拡張ナビゲーションが実行されている場合、コンポーネントがを使用するために、既存の回線では状態
このシナリオに対処するための Blazor フレームワークの追加作業は、.NET 10 (2025 年 11 月) で検討されています。
でサポートされていない回避策の詳細とコミュニティの説明については、「拡張ページナビゲーション (dotnet/aspnetcore
#51584)での永続的なコンポーネントの状態のサポート」を参照してください。 ‡サポートされていない回避策は、Blazor アプリで使用するために Microsoft によって承認されていません。
サードパーティ製のパッケージ、アプローチ、コードは、ご自身の責任で使用してください。
パフォーマンスを低下させる一方で、内部ページ要求の
プリレンダリングのガイドライン
プリレンダリング ガイダンスは、Blazor ドキュメント内で主題別に整理されています。 次のリンクは、主題別に設定された、ドキュメント全体のすべてのプリレンダリング ガイダンスを含んでいます。
基礎
- 概要: クライアントとサーバーのレンダリングの概念
- ルーティング
- 静的ルーティングと対話型ルーティング
- 複数のアセンブリからコンポーネントへのルート: 対話型ルーティング
-
OnNavigateAsync は、プリレンダリング時に 2 回実行されます。
OnNavigateAsync
で非同期ナビゲーション イベントを処理する
- スタートアップ
- 環境: クライアント側で環境を読み取ります。 Blazor Web App
- エラーを処理する: プリレンダリング
- SignalR
コンポーネント
-
プリレンダリング中に
<head>
の内容を制御する - レンダリング モード
- プリレンダリングに関連する Razor コンポーネント ライフサイクルのトピック
-
コンポーネントの初期化 (
OnInitialized{Async}
) -
コンポーネントのレンダリング後 (
OnAfterRender{Async}
) - プリレンダリング後のステートフル再接続
- JavaScript 相互運用を使用したプリレンダリング: このセクションは、.NET からの JavaScript の呼び出しおよび JavaScript からの .NET の呼び出しに関する 2 つの JS 相互運用に関する記事にも記載されています。
- レンダーで不完全な非同期アクションを処理する: サーバーでのプリレンダリング中に実行時間の長いライフサイクル タスクが原因でレンダリングが遅延する場合のガイダンス。
-
コンポーネントの初期化 (
-
QuickGrid
コンポーネント サンプル アプリ: QuickGridサンプル アプリのBlazorは GitHub Pages でホストされます。 コミュニティによって管理されるBlazorWasmPrerendering.Build
GitHub プロジェクトを使用した静的プリレンダリングにより、このサイトは高速に読み込まれます。 - コンポーネントを Razor Pages アプリおよび MVC アプリに統合するときのプリレンダリング
-
プリレンダリング中に
認証と権限承認
- サーバー側での脅威の緩和: クロスサイト スクリプティング (XSS)
- Blazor サーバー側のセキュリティの概要
-
Blazor サーバー側の追加シナリオ: トークンの読み取り
HttpContext
- Blazor WebAssembly 概要: プリレンダリングのサポート
- Blazor WebAssembly その他のシナリオ
- 対話型サーバー側レンダリング: クロスサイト スクリプティング (XSS)
状態管理: プリレンダリングを処理する: "プリレンダリングの処理" に関するセクションに加えて、この記事の他のいくつかのセクションにプリレンダリングに関する解説が含まれています。
.NET 7 以前については、「 Blazor WebAssembly セキュリティの追加シナリオ: 認証を使用したプリレンダリング」を参照してください。 このセクションのコンテンツを表示した後、ドキュメント記事のバージョン セレクタードロップダウンを最新の .NET リリース バージョンにリセットして、ドキュメント ページが後続のアクセス時に最新リリースに読み込まれるようにします。
ASP.NET Core