Blazor in .NET 8 の Auto レンダーモードは、.wasm ダウンロード済みでも、WebAssembly モードで動作しない場合がある

## .NET 8 で強化された Blazor のレンダーモード

.NET 8 の Blazor からは新しく Auto レンダーモードという動作が追加になりました。これは、

- **初めて Auto レンダーモードなページアクセスしたときは Blazor Server** として動作開始。Blazor Server なので "Loading..." みたいな待ち時間を挟むことなく即時にインタラクション可能で動作する。
- その間に、同ページが Blazor WebAssembly として動作するのに必要な**アセンブリファイル (.wasm) をバックグラウンドでダウンロード**。ブラウザのキャッシュストレージに保存。
- **次回、同じ Auto レンダーなページにアクセスしたとき**には、キャッシュストレージにダウンロードしておいたアセンブリファイルを使って、**Blazor WebAssembly** として動作開始。サーバー側リソースを消費することもなく、不安定な通信環境でも安定してブラウザ上で動作する。

という、Blazor Server と Blazor WebAssembly の弱点を相互に補うように動作する新しい動作形態です。

また .NET 8 の Blazor では、ASP.NET Core MVC や Razor Pages と同様のサーバー側レンダリングの動作モード (Server Side Static Rendering, "SSR") も追加されました。さらに加えて、SSR・Server・WebAssembly・Auto の各レンダーモードを、ページやコンポーネント単位で細かく指定し、同一サイト上でもレンダーモードが切り替わりながら動作するようになっています。Server レンダーモードとしての動作の際は、SignalR 接続が開かれますが、これも、必要なときに SignalR 接続を開始、SSR や WebAssembly などのページに移動すると自動でこの SignalR 接続が閉じられる、といった、サーバー側資源を無駄に消費しない最適化も施されています。

...なんだかものすごいことになっていますねw。

しかし Auto レンダーモードで構成されているページで、且つ、アセンブリファイル (.wasm) がキャッシュストレージにダウンロード済みであっても、一定の条件の場合は、**WebAssembly モードではなく Server モードとして動作する場合がある**ことがわかりました。

以下で説明します。

## 動作確認用に Blazor Web アプリケーションを作成

まず、説明用の Blazor Web アプリケーションを作成することにします。標準のプロジェクトテンプレートを使って、以下のオプションを指定して、新規の Blazor Web アプリケーションプロジェクトを生成します。

- Interactive render mode = Auto (対話的レンダーモードは Auto で)
- Interactive location = Per page/component (対話的モードはページかコンポーネント単位で指定)
- Include sample page = Yes (サンプルページを含む)

次に、Client 側プロジェクトで、カウンターコンポーネント `CounterComponent` を以下のように実装します。Blazor Server として動作してるのか Blazor WebAssembly として動作してるのかを、プロセッサアーキテクチャを表示することでわかるようにしているのがポイントです。

```cs:Client 側プロジェクトの Components/CounterComponent.razor
@using System.Runtime.InteropServices

<h1>@Title</h1>

<p>
  <!--
  👇プロセッサアーキテクチャを表示。
     - Blazor Server としての動作なら "X64" などのサーバープロセスのアーキテクチャが、
     - Blazor WebAssembly としての動作なら "Wasm" が
     表示される。 
  -->
  <b>Process Architecture:</b>
  <span>@RuntimeInformation.ProcessArchitecture</span>
</p>

<p role="status">
  Current count: @currentCount
</p>

<button class="btn btn-primary" @onclick="IncrementCount">
  Click me
</button>

@code 
{
    [Parameter]
    public string? Title { get; set; }

    private int currentCount = 0;

    private void IncrementCount()
    {
        currentCount++;
    }
}
```

そして、この `CounterComponent` を、まずは既存の、Auto レンダーモードに指定されている Client 側プロジェクトの `Counter` コンポーネントで使用するように書き換えます。

```cs:Client 側プロジェクトの Pages/Counter.razor
@page "/counter"
@rendermode InteractiveAuto @* 👈 レンダーモードは Auto *@

<CounterComponent Title="Counter (Auto)" />
```

同じようなページを、Server 側プロジェクトに、InteractiveServer レンダーモードで新規追加します。

```cs:Server 側プロジェクトの Components/Pages/CounterServer.razor
@page "/counter-server"
@rendermode InteractiveServer @* 👈 レンダーモードは Server *@

<CounterComponent Title="Counter (Server)" />
```

こうして、**"どのレンダーモードで動作しているか" をプロセッサアーキテクチャで表示するコンポーネントを埋め込んだ、レンダーモード = Auto と、レンダーモード = Server の、2 つのページコンポーネント**ができました。

あとはページナビゲーションのコンテンツを調整して実行します。これで以下のような Blazor Web アプリケーションができました。ナビゲーションには、リンク先の各ページのレンダーモードを付記しておきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/2e953882-ec1c-8ef8-d703-bc688634444f.png)

## SSR ページから Auto レンダーページへ移動する

さて、ではいちど "Counter (Auto)" ページにアクセスしてアセンブリファイル (.wasm) をダウンロードさせておき、改めて "Home (SSR)" ページに戻り、念のため、ブラウザの再読込を行なっておきます。つまり、Auto レンダーモードのページ ("Counter (Auto)") が Blazor WebAssembly モードで動作するはず、の状態にしておきます。

この状態、"Home (SSR)" ページが表示されている状態から、ナビゲーションの "Counter (Auto)" をクリックして、Auto レンダーモードのページに移動してみましょう。すると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/61e15b88-f038-3f50-c52d-25076b4f01ad.png)

プロセッサアーキテクチャには "Wasm" と表示され、この Auto レンダーモードのページが、予想どおり WebAssembly モードで動作していることが確認できました。

## Server レンダーページから Auto レンダーページへ移動する

では次に、いったん、Server レンダーモードのページ ("Counter (Server)") へ移動します。もちろんこのページでは、プロセッサアーキテクチャが "X64" と表示され (※このデモンストレーション環境は Windows 11 64bit 環境なのです)、Blazor Server として動作していることが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/e2426263-813d-9a0a-e6aa-df284c8c8613.png)

面白いのはここからです。

この状態から、Auto レンダーモードのページ ("Counter (Auto)") を再訪問してみましょう。

すると...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/58450/d30b6710-7b07-b314-580b-af2d58dbb29f.png)

なんと、先ほどは予想どおり WebAssebly モードで動作していたのにもかかわらず、**今度は Server モードで動作**しているようです。

## 考察

詳しく裏取りしたわけではないのですが、おそらくはこれは Blazor の仕様ではないかと考えています。Server レンダーモードで動作しているページから Auto レンダーモードのページに遷移した場合は、そこでもし WebAssembly モードでの動作に切り替わるとなると、いくらアセンブリファイルはブラウザのキャッシュストレージに既に保存済みとはいえ、Blazor WebAssembly ランタイムが開始されるまでの若干の遅延があります。既に現状、Server モードして、SingalR 接続済みで動作しているのですから、そこは素早く Server レンダーモードのまま、ページ遷移するほうが好ましいと考えられたのかもしれませんね。

現時点では、この振る舞いを変更、例えば Server レンダーモードのページからの遷移であっても、WebAssembly モードの動作に切り替えて動作することを優先する、みたいなことができるのかどうかは不明です。

## まとめ

Auto レンダーモードのページで、且つ、アセンブリファイル (.wasm) がブラウザのキャッシュストレージに保存済みであっても、Server レンダーモードで動作中のページからの遷移の場合は、WebAssembly モードではなく Server モードのまま動作することがわかりました。

このこと自体が何か困ったことになるとは思われませんが、何かトラブルシューティングの必要が発生した際などは、このような Auto レンダーモードについての振る舞いについてよく知っておくことは役に立つと思います。

_Learn, Practice, Share!_