1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Blazorで特定のページだけSignalRを繋ぎたい

Posted at

この記事は、私の試行錯誤の記録であり、だいぶ良いところまで行ってるものの、道半ばです。
より良いアイデア・ドキュメント等ご存知の方がいらっしゃいましたら、教えてほしいです。

はじめに

Blazor Serverで作ったページを開くと、ブラウザとサーバが自動でSignalRで接続される。
ページ遷移等におけるUIの変更がサーバ側で処理され、DOMの差分がこのSignalRの回線を通してブラウザに届くことで、すごいいい感じにUIが更新される。

しかし!タブブラウザとかでタブが休眠すると、SignalRが切れ、再接続がイマイチだったりして、いいところばっかじゃない。

動的な画面更新とか、フォームとかはSignalR繋がっても良いけど、そうでないページはSignalR繋がないことはできるのか?という話。

↑上記の記事を参考にしました。この記事では、SignalRが完全に使えない状況を想定していて、すべてのページを静的にしている。
今回想定しているのは、SignalR使えなくはないけど、必要に応じてのみ使いたい状況。

とりあえずの解決

まず、Blazor Serverのプロジェクトを新規作成しましょう。一応.Net6のテンプレートで説明を進めます。まぁ、.Net7も大体いっしょ。

修正場所は、以下の2点。
まず、SignalRの接続をつかさどっている大元、Pages/_Layout.cshtmlの下の方にあるJavaScriptの部分。これを、必要な時のみ読み込ませる(レンダリングする)ようにすればよい。

<script src="_framework/blazor.server.js"></script>

次に、Pages/_Host.cshtmlの、コンポーネントのレンダリング開始場所。このrender-modeを調整することで、サーバのふるまいを調整する。

<component type="typeof(App)" render-mode="ServerPrerendered" />

①_Host.cshtmlの修正

リクエストの時に呼び出される順番は、_Hostが先なので、まずこっちから修正する。

@{
    Layout = "_Layout";

    var mode = RenderMode.Static;
    var path = Request.Path.Value!;
    var page = new[] { "/create", "/edit", "/delete" };
    if (page.Any(t => path.Contains(t, StringComparison.OrdinalIgnoreCase))) mode = RenderMode.Server;

    ViewBag.RenderMode = mode;
}

<component type="typeof(App)" render-mode="@mode" />

リクエストされたURLによって、render-modeを変えるようにした。

render-modeについては以下の通り。

  • RenderMode.ServerPrerendered
    • 先にサーバでレンダリングし、ブラウザに完成したHtmlを送る。
      ブラウザはそれを表示した後にSignalR接続を開始し、接続後もう一度レンダリングする。以後、UIの変更はSignalR接続により処理される。つまり初回接続時は、2回レンダリングされる。
      (JavaScriptを実行できない)検索サイトのクローラとかでも、ページのコンテンツを取得できる利点がある。
  • RenderMode.Static
    • 先にサーバでレンダリングし、ブラウザに完成したHtmlを送る。
      ブラウザはそれを表示した後SignalR接続を開始するが、接続を介してのレンダリングはしない。SignalR接続されるので、接続を介してのレンダリングはしないものの、接続が切れたりすると再接続が求められる。
  • RenderMode.Server
    • 先にレンダリングしない。ブラウザには、テンプレート部分だけ送られる。
      ブラウザはそれを表示した後SignalR接続を開始し、コンテンツ部分をレンダリングする。レンダリングは1回。
      JavaScriptが動かないと、画面にテンプレート部分しか表示されない。

今回の例では、普通のページをRenderMode.Staticにし、フォームのページをRenderMode.Serverにしている。
フォームのページは、SignalR接続しなきゃ機能を果たさないし、検索エンジン対策する必要もないので、ServerPrerenderedではなくServerにしている。レンダリング1回になるので、その方が都合がよい。

ここでrender-modeをどれにしても、SignalRは接続される。Staticにしたときは、接続しないようにしたい。
そこで、modeをViewBagに突っ込んで、次へ…

②_Layout.cshtmlの修正

@if (ViewBag.RenderMode != RenderMode.Static)
{
    <script src="_framework/blazor.server.js"></script>
}

このJavaScriptさえ読み込ませなければ、SignalRは接続されない。そこで、ViewBagの中身を確認して、Staticの時にそもそもこの行をレンダリングしないようにする。
これでSignalRが接続されなくなって、うれしい!

現状の問題

上述した方法だと、問題がある。

  • RenderModeがStaticのページのみを遷移しているとき…
    • 遷移時に毎回サーバにリクエストされて完璧。
  • 一旦Staticではないページ(フォームとか)に入っちゃうと…
    • そこでSignalR接続され、以後Staticであってほしいページに戻っても、回線を使ってコンポーネント以下でUIが更新されるようになってしまう。

NavigationManagerで強制移動してリロードさせちゃえば、またStaticに戻るんだけど、すべてのリンクでやるのはめんどくさい…
Staticなページがメインなサイト(管理者しかフォームのページに行かない…)とかなら、とりあえずこれで良いかな…

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?