support-prerendering について
インタラクティブな Blazor アプリでプリレンダリングを扱う開発者向けのスキルです。状態の保持、個別のプリレンダリング無効化、インタラクティブ実行環境に依存するコードを解説します。
主な機能
- 01プリレンダリングのライフサイクル解説
- 02レンダリング間の状態保持
- 03プリレンダリングの個別制御
- 04HttpContext 用の静的ルーティング
- 05実行時のインタラクティブ状態検出
- 06プリレンダリング用クライアントサービス設定
活用シーン
- 01データの重複取得の防止
- 02レンダリング後のブラウザー API 利用
- 03HttpContext が必要なページの提供
- 04インタラクティブ時のみの初期化処理
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dotnet/ skills --skill support-prerenderingインストール後、Claude Code で/support-prerenderingを入力して読み込まれたか確認します。
詳細
プリレンダリングのサポート
プリレンダリングの仕組み
プリレンダリングは、すべてのインタラクティブレンダリングモードでデフォルトで有効です。サーバーがコンポーネントを静的 HTML としてレンダリングし、すぐにブラウザーへ送信します。その後、インタラクティブランタイム(Server/WebAssembly)が読み込まれ、完全なインタラクティブ機能を備えた状態でコンポーネントを再レンダリングします。
つまり、次のようになります。
OnInitializedAsyncは2 回実行されます。プリレンダリング(静的)のときと、インタラクティブランタイムがアタッチされたときにそれぞれ実行されます。- プリレンダリング中に
OnAfterRenderAsyncは呼び出されません。インタラクティブレンダリング後にのみ呼び出されます。 - インタラクティブページ間の内部ナビゲーション(インタラクティブルーティング)では、プリレンダリングがスキップされます。プリレンダリングはページ全体の読み込み時にのみ行われます。
ステップ 1 — プロジェクトの AGENTS.md を読む
プロジェクトの AGENTS.md で、Interactivity Mode と Interactivity Scope を確認します。
| モード | プリレンダリングは適用されるか |
|---|---|
| None(静的 SSR) | いいえ — インタラクティブへの引き継ぎがありません |
| Server | はい |
| WebAssembly | はい |
| Auto | はい |
モードが None の場合、このスキルは適用されません。
プリレンダリングからインタラクティブへの状態の維持
最もよくあるプリレンダリングの問題は、プリレンダリング中に OnInitializedAsync で読み込んだデータが破棄され、インタラクティブランタイムのアタッチ時に再取得されることです。これにより、ちらつきや API/DB 呼び出しの重複が発生します。
推奨:[PersistentState] 属性
プロパティに注釈を付けると、プリレンダリング中に自動的にシリアル化され、インタラクティブ化時に復元されます。
@page "/forecasts"
@rendermode InteractiveServer
<h1>Weather</h1>
@if (Forecasts is null)
{
<p>Loading...</p>
}
else
{
@foreach (var f in Forecasts)
{
<p>@f.Date: @f.TemperatureC°C</p>
}
}
@code {
[PersistentState]
public WeatherForecast[]? Forecasts { get; set; }
protected override async Task OnInitializedAsync()
{
Forecasts ??= await ForecastService.GetForecastsAsync();
}
}
??= パターンが重要です。これは「プロパティがプリレンダリング状態からすでに復元されている場合は、取得しない」という意味です。
同じコンポーネントの複数のインスタンス
同じコンポーネント型が複数回表示される場合は、@key を使って状態を区別します。
@foreach (var item in items)
{
<ItemCard @key="item.Id" />
}
上級者向け:PersistentComponentState サービス
複雑なケース(動的キー、カスタムシリアル化)では、命令型 API を使用します。
@inject PersistentComponentState ApplicationState
@code {
private List<Order>? orders;
protected override async Task OnInitializedAsync()
{
ApplicationState.RegisterOnPersisting(PersistOrders);
if (!ApplicationState.TryTakeFromJson<List<Order>>("orders", out var restored))
{
orders = await OrderService.GetOrdersAsync();
}
else
{
orders = restored;
}
}
private Task PersistOrders()
{
ApplicationState.PersistAsJson("orders", orders);
return Task.CompletedTask;
}
}
プリレンダリングを無効にする
コンポーネントがブラウザー API に即座に依存する場合や、プリレンダリングとインタラクティブレンダリングの二重レンダリングによる問題を [PersistentState] で解決できない場合は、プリレンダリングを無効にします。
コンポーネント定義で指定する
@rendermode @(new InteractiveServerRenderMode(prerender: false))
必要に応じて、InteractiveServerRenderMode を InteractiveWebAssemblyRenderMode または InteractiveAutoRenderMode に置き換えます。
コンポーネントのインスタンスで指定する
<MyChart @rendermode="new InteractiveServerRenderMode(prerender: false)" />
アプリ全体で指定する
App.razor で設定します。
<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />
<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />
注:親のプリレンダリング設定は子に優先します。<Routes> でプリレンダリングを無効にすると、個々のページで再度有効にすることはできません。
インタラクティブルーティングからページを除外する
全体がインタラクティブなアプリでは、HttpContext(Cookie、リクエストヘッダー、レスポンスステータスコード)を必要とするページがある場合があります。これらのページは、インタラクティブランタイム内ではなく、静的 SSR でレンダリングする必要があります。
[ExcludeFromInteractiveRouting] を使用します。
@page "/privacy"
@attribute [ExcludeFromInteractiveRouting]
<h1>Privacy Policy</h1>
これにより、このページへの移動時にページ全体が再読み込みされ、インタラクティブルーティングから抜けます。ページは HttpContext に完全にアクセスできる状態で、静的 SSR としてレンダリングされます。
App.razor でレンダリングモードを条件付きで適用します。
<!DOCTYPE html>
<html>
<head>
<HeadOutlet @rendermode="RenderModeForPage" />
</head>
<body>
<Routes @rendermode="RenderModeForPage" />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
@code {
[CascadingParameter]
public HttpContext HttpContext { get; set; } = default!;
private IComponentRenderMode? RenderModeForPage =>
HttpContext.AcceptsInteractiveRouting() ? InteractiveServer : null;
}
InteractiveServer を、アプリで設定されているレンダリングモードに置き換えます。
実行時にプリレンダリングとインタラクティブ状態を判定する
RendererInfo を使用して、インタラクティブ時にのみ実行するコードをガードします。
protected override async Task OnInitializedAsync()
{
if (RendererInfo.IsInteractive)
{
// Only runs during the interactive render, not during prerender
await StartSignalRConnection();
}
}
RendererInfo のプロパティ:
IsInteractive— プリレンダリング中はfalse、インタラクティブランタイムのアタッチ後はtrueName— プリレンダリング中は"Static"、インタラクティブ時は"Server"または"WebAssembly"
プリレンダリング中にクライアントサービスが失敗する
.Client プロジェクト内のコンポーネントは、サーバー上でプリレンダリングされます。クライアントの Program.cs にのみ登録されているサービス(例:IWebAssemblyHostEnvironment)は、プリレンダリング中に利用できません。
次のいずれかの方法で解決します。
- サーバーに対応するサービスを登録する — 両方の
Program.csファイルでサービスを提供します - サービスを省略可能にする — nullable なデフォルト値を使ったコンストラクターインジェクションを使用します:
public MyComponent(IMyService? svc = null) - サービス抽象化を作成する — インターフェイスを
.Clientに配置し、両方のプロジェクトに実装を用意します - そのコンポーネントのプリレンダリングを無効にする
やってはいけないこと
OnInitializedAsyncで JS 相互運用を呼び出さないでください。プリレンダリング中は JS を利用できません。OnAfterRenderAsync(firstRender)を使用してください。OnInitializedAsyncが一度だけ実行されると想定しないでください。プリレンダリングでは 2 回実行されます。必ず[PersistentState]または??=によるガードを使用してください。- インタラクティブコンポーネントで
HttpContextを使用しないでください。静的プリレンダリング中にのみ利用でき、インタラクティブなライフサイクル中は利用できません。必要なページには[ExcludeFromInteractiveRouting]を使用してください。 - 最初の手段としてプリレンダリングを無効にしないでください。体感的な読み込み速度と SEO に悪影響があります。代わりに
[PersistentState]を使用して状態を保持してください。