注意
これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .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 アプリのページまたはビューに埋め込まれたコンポーネントの場合は、コンポーネントの状態保持タグ ヘルパー) を使用してプリレンダリングされたページ内の状態の永続化をサポートしています。
プリレンダリングされた状態を保持するには、PersistentComponentState サービスを使用して、どの状態を永続化するかを決定します。 アプリが一時停止される前に、PersistentComponentState.RegisterOnPersisting によってコールバックが登録され、コンポーネントの状態が保持されます。 状態は、アプリの再開時に取得されます。 アプリのシャットダウン中に競合状態になる可能性を回避するために、初期化コードの最後に呼び出しを行います。
次の例は、一般的なパターンを示しています。
{TYPE}
プレースホルダーは、永続化するデータの種類を表します。
{TOKEN}
プレースホルダーは、状態識別子の文字列です。 nameof({VARIABLE})
の使用を検討してください。ここで、{VARIABLE}
プレースホルダーは状態を保持する変数の名前です。 状態識別子に nameof()
を使うと、引用符で囲まれた文字列の使用が回避されます。
@implements IDisposable
@inject PersistentComponentState ApplicationState
...
@code {
private {TYPE} data;
private PersistingComponentStateSubscription persistingSubscription;
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<{TYPE}>(
"{TOKEN}", out var restored))
{
data = await ...;
}
else
{
data = restored!;
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistData);
}
private Task PersistData()
{
ApplicationState.PersistAsJson("{TOKEN}", data);
return Task.CompletedTask;
}
void IDisposable.Dispose()
{
persistingSubscription.Dispose();
}
}
次のカウンター コンポーネントでは、プリレンダリング中にカウンターの状態を保持し、コンポーネントを初期化するためにその状態を取得します。
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 回だけ設定されます。 この値は、コンポーネントが再レンダリングされると復元されます。 以下に、出力の例を示します。
注意
アプリが対話型ルーティング 採用し、ページに内部 拡張ナビゲーション経由で到達した場合、プリレンダリングは行われません。 したがって、次の出力を表示するには、PrerenderedCounter2
コンポーネントのページ全体の再読み込みを行う必要があります。 詳細については、「対話型ルーティングとプリレンダリングの」セクションを参照してください。
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 アプリのページまたはビューに埋め込まれたコンポーネントに対しては、アプリのレイアウトの終了 </body>
タグ内に、<persist-component-state />
HTML タグを含む Persist Component State Tag Helperを追加する必要があります。 これは、Razor Pages アプリと MVC アプリでのみ必要です。 詳細については、「ASP.NET Core でのコンポーネントの状態保持タグ ヘルパー」を参照してください。
Pages/Shared/_Layout.cshtml
:
<body>
...
<persist-component-state />
</body>
Routes
コンポーネントでレンダリング モードが定義されていない場合、アプリはページ単位/コンポーネントごとの対話機能とナビゲーションを使用します。 ページ単位またはコンポーネント単位のナビゲーションを使用すると、内部†ナビゲーションは、アプリが対話型になった後 拡張ルーティング によって処理されます。 †この文脈での内部 とは、ナビゲーション イベントの URL の宛先がアプリ内の Blazor エンドポイントであることを意味します。
PersistentComponentState サービスは、初期ページ読み込みでのみ機能し、内部拡張ページ ナビゲーション イベントでは機能しません。
永続的なコンポーネントの状態を利用するページへの完全な (拡張されていない) ナビゲーションをアプリが実行すると、永続化された状態が対話型になったときにアプリで使用できるようになります。
対話型回線が既に確立されていて、永続的なコンポーネントの状態を利用するページへの拡張ナビゲーションが実行されている場合、コンポーネントがを使用するために、既存の回線では状態 使用できなくなります。 内部ページ要求のプリレンダリングはなく、PersistentComponentState サービスは拡張ナビゲーションが発生したことを認識していません。 既存の回線で既に実行されているコンポーネントに状態更新を配信するメカニズムはありません。 その理由は、Blazor はランタイムの初期化時にサーバーからクライアントへの状態の受け渡しのみをサポートし、ランタイムの開始後にはサポートされないためです。
このシナリオに対処するための Blazor フレームワークの追加作業は、.NET 10 (2025 年 11 月) で検討されています。 でサポートされていない回避策の詳細とコミュニティの説明については、「拡張ページナビゲーション (dotnet/aspnetcore
#51584)での永続的なコンポーネントの状態のサポート」を参照してください。 ‡サポートされていない回避策は、Blazor アプリで使用するために Microsoft によって承認されていません。 サードパーティ製のパッケージ、アプローチ、コードは、ご自身の責任で使用してください。
パフォーマンスを低下させる一方で、内部ページ要求の による状態の読み込みの問題を回避する拡張ナビゲーションを無効にする方法については、コア ルーティングとナビゲーションASP.NET で説明します。
プリレンダリング ガイダンスは、Blazor ドキュメント内で主題別に整理されています。 次のリンクは、主題別に設定された、ドキュメント全体のすべてのプリレンダリング ガイダンスを含んでいます。