プロジェクト作って眺めてみようと思います。
.NET Core 3.0 Preview 6 + Visual Studio 2019 Preview + Blazor 拡張機能で試しています。
.NET Core 3.0 Preview 6 で、@functions
から @code
に変わったりと細かい変更がちょくちょくありました。さらには認証にも対応(個人的にこれ嬉しい)したみたいで、なんだか本格的に必要な機能セットが揃ってき始めてる気がします。
詳細は以下のページで。
さて、クライアントサイドの Blazor といっても 2 種類のプロジェクトがあって Blazor (ASP.NET Core hosted) と Blazor (client-side) があります。デプロイのハードルが低そうなのがとりあえず ASP.NET Core hosted なので、そっちから行ってみようと思います。
新規作成すると 3 つのプロジェクトが作られます。
上から Blazor のプロジェクト(.NET Standard 2.0)、ASP.NET Coreの プロジェクト(.NET Core 3.0)、共有したいクラスを入れるプロジェクト(.NET Standard 2.0)になります。
実行すると Blazor のテンプレートには Hello world のページとボタンを押すとカウントアップするページと表形式でデータを表示するページがあるアプリが立ち上がります。サーバーサイド Blazor と大きく異なるのは、ちゃんと REST API を叩いてるところです。 ASP.NET Core のほうのプロジェクトの Controllers フォルダーを見ると REST API が作られてます。
そして、クライアント側のプロジェクトの Pages/FetchData.razor
を見るとちゃんと REST API を叩いています。
WeatherForecast[] forecasts; protectedoverride async Task OnInitAsync() { forecasts = await Http.GetJsonAsync<WeatherForecast[]>("api/SampleData/WeatherForecasts"); }
この Http はページの上部で @inject HttpClient Http
という形で定義されていて、DI で HttpClient をもらってます。いいね。
サーバーのスタートアップロジック
サーバーのほうの Startup.cs を覗いてみると Blazor に関わりそうな処理が Configure メソッドにいくつかあります。
app.UseResponseCompression(); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBlazorDebugging(); } app.UseClientSideBlazorFiles<Client.Startup>(); app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html"); });
恐らく UseResponseCompression で、レスポンスの圧縮してね♪的なことが以下のドキュメントに書いてあるので、そこらへんのためだと思います。
あとは、UseClientSideBlazorFiles でクライアントサイドの Blazor のファイルを指定して、MapFallbackToClientSideBlazor で クライアント側のプロジェクトの wwwroot/index.html
に処理を回すようになってるように見受けられます。
index.html は以下のようになっていて blazor.webassembly.js
を読み込んでいます。このファイルをおしゃれにすればアプリが立ち上がるまでの間のユーザーのイライラを多少緩和出来るっぽい?
<!DOCTYPE html><html><head><metacharset="utf-8" /><metaname="viewport"content="width=device-width" /><title>ClientSideBlazorHelloWorld</title><basehref="/" /><linkhref="css/bootstrap/bootstrap.min.css"rel="stylesheet" /><linkhref="css/site.css"rel="stylesheet" /></head><body><app>Loading...</app><scriptsrc="_framework/blazor.webassembly.js"></script></body></html>
ここで app タグを指定していて、この app タグはクライアントサイドの Startup.cs で AddComponent の部分で App クラス(ファイルとしては App.razor)であるということがわかります。
using Microsoft.AspNetCore.Components.Builder; using Microsoft.Extensions.DependencyInjection; namespace ClientSideBlazorHelloWorld.Client { publicclass Startup { publicvoid ConfigureServices(IServiceCollection services) { } publicvoid Configure(IComponentsApplicationBuilder app) { app.AddComponent<App>("app"); } } }
App.razor を見ると Blazor のルーティングの定型文が書いてあります。
<Router AppAssembly="typeof(Program).Assembly"><NotFoundContent><p>Sorry, there's nothing at this address.</p></NotFoundContent></Router>
Router は、_Imports.razor で自動的に using されている名前空間の中にあるクラスです。
@using System.Net.Http @using Microsoft.AspNetCore.Components.Forms @using Microsoft.AspNetCore.Components.Layouts @using Microsoft.AspNetCore.Components.Routing // これ! @using Microsoft.JSInterop @using ClientSideBlazorHelloWorld.Client @using ClientSideBlazorHelloWorld.Client.Shared
そして、Pages の中の各種ページにルーティングされて、初期状態のパスの /
に紐づくのは Index.razor なので、 Index.razor の中身が表示されます。Pages フォルダーの中の _Imports.razor では @layout MainLayout
だけが書いてあり、ここでこのフォルダーの中身は基本的に MainLayout.razor をレイアウトファイルとして使うことがわかります。
MainLayout.razor は Shared フォルダーにあって以下のようになっています。
@inherits LayoutComponentBase <divclass="sidebar"><NavMenu /></div><divclass="main"><divclass="top-row px-4"><ahref="http://blazor.net"target="_blank"class="ml-md-auto">About</a></div><divclass="content px-4"> @Body </div></div>
この @Body
の部分に今回は Index.razor が入る感じですね。NavMenu は NavMenu.razor にありますが、こちらは NavLink (これは Blazor 側で提供されてるクラス)を使ってメニュー作ってるだけです。fetchdata, counter などへの遷移するためのメニューです。
まとめ
とりあえずさらっと眺めてみました。 理解あってるかなぁ。