C#でフロントWebアプリが動く、Blazorを試してみた

こんにちは。 正月から風邪が長引いていて週末はヒッキーな青木です。

普段はバックエンド要素多めなので 久しぶりにフロントエンドも見てみたいってことで 今回はマイクロソフト一押し?のBlazorを試してみることにします!

Blazorを調べてみることにしたキッカケ

  • C#でフロントエンド書けるのは素敵じゃね?
  • VueやReactを使ったJS(or Typescript)以外の選択肢がそろそろ欲しくなってきた
  • 今後ブレイクしそう?なWebAssemblyを触りだけでも体験しておきたい

です。

特に3点目のWebAssemblyに対応しているのが個人的にはイイですね。 WebAssemblyってまともなFrameworkないっぽいし、
Blazorはどうなんだろうって思った次第であります!

この記事はBlazor WebAssembly中心について説明していますが現時点で<u>プレビュー段階</u>のものです。(2020年5月GA予定)
今はもう一つの実装方法である、Blazor Serverというサーバーサイドの方式が正式GAされているため、オススメだそうです。

Blazorとは

Blazorは、JSではなくC#でSPAを構築するためのフレームワーク。 マイクロソフトが開発しているので、.NETベースです。

Blazorの特徴

C#でかけて、.NETのライブラリが活用できるのが推しポイントかなと思います。

  • クライアントサイド(WebAsembly)とサーバーサイド形式の2種類のホスティングモデルがある(後述)
  • Frontend、BackendをC#で一貫して書ける
  • 必要に応じてC#からJSコードを呼び出すことができる
  • .Net Standardのライブラリがそのまま活用できる
  • コンポーネントベースの開発
  • ModelはFrontend / Backendで共通化できる
  • ViewはMSのRazor

2種類のホスティングモデルについて

(1)Blazor WebAssembly

現時点でプレビュー段階です。2020年5月にGA予定です。

WebブラウザのWebAssembly上に.NETのコードを実行するモデルです。

ページにアクセスすると、JSではなく、 .NETランタイムやアプリコードのDLLでダウンロードされて WebAssemblyを介して.NETコードを実行するイメージでしょうか。

さらにJavaScript 相互運用というのがあって、 C#のコードからJSを呼んだりその逆もできるようです。 .NETライブラリで完結できない部分はどうしても出てくるでしょうから、 その場合の保険として良さそうです。

あともう一つ良いのは実行環境だけでなく、 BlazorはFrameworkをセットで提供しているのが良いですね。 従って、コンポーネントをひたすら実装する感じで WebAssemblyであるこは意識する必要はなさそうです。

(2)Blazor Server

.NET Core3.0から正式サポートされました

Blazor ServerはサーバーサイドでDOMを構築するパターンです。 UIの更新はSignalRの接続を介して行われていて、変更があったときに差分のみDOMを反映します。

現時点ではこっちが推奨だそうですが、WebAssenbly側を調べたいので割愛します。

早速、Blazor WebAssemblyをHello worldしてみる

環境

  • Mac
  • Visual Studio Code

クロスプラットフォームに対応しているので、Windows / Mac / Linuxでちゃんと動くと思います。

Install

Blazor WebAssembly実行するためには、.Net Core 3.1(Preview)のSDKをインストール しておく必要があります。

執筆時点では、3.1.100をインストールしました

dotnet.microsoft.com

SDKをインストールすると、dotnet CLIが実行できるようになります。

Blazorのテンプレートをインストールします。

dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.1.0-preview4.19579.2

Blazorのテンプレートで、Blazor WebAssemblyプロジェクトを作成

dotnet new blazorwasm -o WebAsm

Blazor WebAssemblyを実行する

このコマンドでソースのビルド&実行します。

dotnet run

ビルドと実行が完了すると、このようにlistenくれるようになりますので ブラウザでhttp://localhost:5000へアクセス。

info: Microsoft.Hosting.Lifetime[0]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.

さくっとHello Worldのページが出てきましたね。 更にサンプルページとしてカウントアップと一覧ページをつけてくれています。 実装の参考に良さそうです!

f:id:ecb_yaoki:20191224192442p:plain
Hello World!

ネットワークを確認してみると、 .NETランタイム(DLL)たちが大量にDLされまくっています

f:id:ecb_yaoki:20191224195039p:plain
ネットワーク

中身を覗いてみる

WebAsm
├── App.razor
├── Pages
│   ├── Counter.razor
│   ├── FetchData.razor
│   └── Index.razor
├── Program.cs
├── Shared
│   ├── MainLayout.razor
│   ├── NavMenu.razor
│   └── SurveyPrompt.razor
├── Startup.cs
├── WebAsm.csproj
├── _Imports.razor
└── wwwroot
    ├── index.html

ざっくりプログラムの動きを把握してみる

ポイントになりそうなところだけ、ピックアップしてみてみます。

Startup.cs

Startupはアプリケーションの初期化ロジックが含まれるコードになっています。 ここではAppというルートコンポーネントの追加をしています。

        public void Configure(IComponentsApplicationBuilder app)
        {
            app.AddComponent<App>("app");
        }



App.razor

ページのルーティングは、Routerコンポーネントで行います。 ルーティングの存在有無によってコンテンツを出し分けています。

ルーティングした際のデフォルトのレイアウトはここではMainLayoutとして定義していました。 特定のレイアウトに変更したい場合は、/Pages/*.razor@layout XXXXLayoutのように上書きすることができます。

<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="@typeof(MainLayout)">
            <p>Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>



/Pages/*.razor

各ページの実装は、/Pages配下で管理しています。 Counter.razorを例に取ると、@codeの中でC#で記述し、 DOMは@onclick=IncrementCountとすることでイベントとメソッドの連携をしています。

@page "/counter"

<h1>Counter</h1>

<p>Current count: @currentCount</p>

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

@code {
    private int currentCount = 0;

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

気になったこと

ちょっと起動が遅い

初回起動に時間かかりすぎなような。 DLLのダウンロード、WebAssemblyに実行するまでの時間が掛かっているってことでしょうか・・・? 一回表示さえしてしまえば、サクサクなのでちょっと残念ですね。

状態管理の扱いがよくわからない

コンポーネントに跨ってModelを共有したい場合はどうやって持つのが正解?その場合の更新方法は?・・。 このあたり誰か教えてほしいっす・・

※ よく見てみたら、MSDocに近日公開予定とありました。そのうち載るかな? docs.microsoft.com

Blazorはコンポーネントや状態管理のベストプラクティスな設計については今後といったところですかねー。

開発環境はまだ整ってなさげ?

こちらも今後に期待で。

  • Visual Studio Codeでコンポーネントのインテリセンスが効かない
  • webpackのwatchモード相当がなさそう

まとめ

C#でSPAが書けるのはフロントエンドの選択肢が単純に拡げられるので良いですね!

一方で、開発環境やパフォーマンスだったり改善できるところはいろいろありそうなので、 そのあたりは今後に期待したいと思います。

次回は実際にページを作ってあそんでみようかな。 それではー!

~ecbeingでは新しいものが出るとつい試してしまうような、好奇心旺盛なエンジニアを大募集中です!~ careers.ecbeing.tech