📝

.NET 8 の Blazor で InteractiveWebAssembly でプリレンダリングをなるべく避けたい

2023/11/23に公開

はじめに

1 つ前の記事でページのレイアウトのレベルで WASM/Server/SSR を切り替える方法について書きました。

https://zenn.dev/microsoft/articles/aspnetcore-blazor-dotnet8-switch

その際にプリレンダリングがあるせいで WASM で作っても一度はサーバーサイドでレンダリングされるので、サーバーでも WASM でも動かないといけないという制約があると書きました。

プリレンダリングを切ると、SSR などの世界から WASM に切り替わる際に WASM が立ち会がるまではページ全体が真っ白になってしまうという問題があるということを書きました。

今回はその解決方法について書きたいと思います。

やり方

前回の記事では SSR/Server 側と WASM 側でレイアウトに同じファイルを使っていましたが、今回の目的を達成するには WebAssembly は WebAssembly 専用のレイアウトが必要なので WasmMainLayout.razor を Client 側のプロジェクトに追加しました。もともと Client 側に置いていた MainLayout.razor はサーバー側のほうで使うので Client プロジェクトからサーバーの方のプロジェクトに移動させます。

そして、WasmMainLayout.razor を以下のようにします。

WasmMainLayout.razor
@using System.Runtime.InteropServices
@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <main>
        <div class="top-row px-4">
            <a href="https://learn.microsoft.com/aspnet/core/" target="_blank">About</a>
        </div>

        <article class="content px-4">
            <div>@(OperatingSystem.IsBrowser() ? "WASM" : "SSR")</div>
            @if (OperatingSystem.IsBrowser())
            {
                @Body
            }
            else
            {
                <div>Loading...</div>
            }
        </article>
    </main>
</div>

<div id="blazor-error-ui">
    An unhandled error has occurred.
    <a href="" class="reload">Reload</a>
    <a class="dismiss">🗙</a>
</div>

ポイントはブラウザー上(WASM 上)で実行されていない場合は <div>Loading...</div> のような読み込み中を表すようなコンテンツを表示してブラウザー上で実行されたときだけレイアウトの @Body を表示するようにしているところです。

こうすることでプリレンダリングのタイミングではページは読み込まれません。そのためページ自体のサーバーサイドでのレンダリングがされなくなるためページは WASM での動作に集中することが出来ます。

SEO などのメリットは無くなってしまいますが、それなりの速度で最初は表示して、その後 WASM が動き出したらページを出すといったことが出来ます。

WasmRouter.razor では、このレイアウトファイルを使うように変更します。

WasmRouter.razor
@using BlazorApp41.Client.Layout
@using BlazorApp41.Client.Pages
@rendermode InteractiveWebAssembly

<Router AppAssembly="@typeof(Counter).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(WasmMainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
</Router>

実行すると以下のようになります。

SSR のタイミングでは Loding... となっているのでいい感じです。そのまま少し待つと WASM に切り替わってページのコンテンツが表示されます。

ばっちりですね。ページの OnInitialized は 1 回しか呼ばれないので WASM しか想定していないページがうっかりサーバー側で動いて例外が発生してしまうことも気にしなくてよくなります。

まとめ

ということで WASM のページでプリレンダリングに対応したくないときの逃げ方について書いてみました。この方法だとレイアウトなどはプリレンダリングされるので、割とさくっとユーザーに表示されてページコンテンツだけが後で表示されるという動きを実現できます。

検索エンジンのボットに対してきちんとコンテンツを表示しないといけないようなケースでは、大変ですが WASM の場合でもちゃんとサーバーサイドでのプリレンダリングに対応したコードを書く様にする必要があります。そうじゃない場合には、今回のように SSR の時にはページを出すのではなく固定の読み込み中を表す UI を出しておく形で良いと思います。

それでは楽しい Blazor 生活を。

まとめ

Microsoft (有志)

Discussion