.NET 8 の Blazor の InteractiveAuto の挙動について
ここでは InteractiveAuto の挙動について直感とは若干反する動きをする部分について書きたいと思います。
私も最初は勘違いしてました。C# の Discord の Web のチャンネルでも過去に話題になったりしてました。
本題
.NET 8 の ASP.NET Core Blazor でページ単位やコンポーネント単位で以下のレンダリングモードを指定できます。厳密には Interactive がついているものにはプリレンダリングがオフのものものもあるので、合計 7 つのレンダリングモードがあることになりますが、今回は簡単にするためにデフォルトの挙動のプリレンダリングありのものを除いた 4 つのレンダリングモードで考えます。
- デフォルトの静的サーバーサイドレンダリング (Static SSR)
- InteractiveServer モード
- InteractiveWebAssembly モード
- InteractiveAuto モード
InteractiveServer モードは、ユーザーとの対話操作を行うたびに SignalR の接続を介してサーバーサイドで処理が行われて、その結果がクライアントに返されて画面が更新されます。InteractiveWebAssembly モードはユーザーとの対話操作の処理は WebAssembly 上で実行されている .NET のランタイム上で動いているアプリケーションで処理されます。
InteractiveServer モードは初回表示の時の起動が早いかわりにサーバーサイドにステートを持っている状態になるためスケーラビリティ的には弱くて、InteractiveWebAsembly モードは初回表示のタイミングで WebAssembly がダウンロードされてから起動するためどうしても遅くなります。プリレンダリングで画面の表示は早いように見せかけることはできますが、その後、WebAssembly 上でアプリが起動するまではページは一切の操作を受け付けません。なまじ画面が表示されてしまっているぶん、ユーザーにはクリックしたりしても無応答の画面が表示されることになります。ただし、すべてがクライアントサイドで実行されるため InteractiveServer に比べるとスケーラビリティの面では有利です。ただ、DB の更新などを行うような処理は別途 Web API として公開してそれを実行するようにしないといけません。
InteractiveAuto は、初回アクセス時には InteractiveServer で動作をして裏で WebAssembly のダウンロードを行い、2 回目以降のアクセスでは InteractiveWebAssembly で動作をするモードです。両方のいいとこどりを目指している感じですね。ただし、両方のモードで動くように実装をしないといけないので、ちゃんと実装しようと思うと結構大変だと思います。
そして、もう 1 つややこしい問題として InteractiveServer のページから InteractiveAuto のページに遷移したときには必ず InteractiveServer で動作します。これはドキュメントにも以下のように記載があります。
自動レンダリング モードでは、ページに既に存在するコンポーネントのレンダリング モードが動的に変更されることはありません。 自動レンダリング モードは、コンポーネントでどの種類のインタラクティビティが使われるかを最初に決定して、その後、コンポーネントがページ上にある限りその種類のインタラクティビティが維持されます。 この最初の決定における 1 つの要素は、WebAssembly/サーバーのインタラクティビティを備えたコンポーネントがページ上に既に存在するかどうかを考慮することです。 自動モードでは、既存の対話型コンポーネントのレンダリング モードと一致するレンダリング モードが優先的に選ばれます。 自動モードが既存のインタラクティビティ モードの使用を優先する理由は、既存のランタイムと状態を共有しない新しい対話型ランタイムの導入を避けるためです。
ということで以下のような流れです。
普通のケース
InteractiveServer のページから InteractiveAuto のページに遷移した場合
InteractiveWebAssembly のページから InteractiveAuto のページに遷移した場合
試してみよう
ということで試してみます。
/wasm
というパスでアクセスできる InteractiveWebAssembly
のページと /server
というパスでアクセスできる InteractiveServer
のページを作成します。そして InteractiveAuto を有効にしてデフォルトの作成されるサンプルページの /counter
にアクセスして様子を確認してみます。どちらのレンダリングモードなのかわかるように /counter
のページには以下のようにちょっとコードを追加して現在のモードを判定してタイトルに表示するようにしました。
@page "/counter"
@rendermode InteractiveAuto
<PageTitle>Counter</PageTitle>
<h1>Counter(@(OperatingSystem.IsBrowser() ? "WASM" : "Server"))</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
この判定ロジックだと Blazor Server の時とプリレンダリングの時を区別できませんが、まぁ今回はそこまで細かいことは気にしないのでよしとしてます。動かしてみた結果が以下のようになります。
初回は Counter ページに行っても InteractiveAuto なので Server で動きます。その後は前のページが InteractiveServer の場合は Server で動いて、それ以外のときは WASM で動いていることが確認できます。
まとめ
すべてを理解してつかいこなすのは難しい…。けど楽しい。
Discussion