Blazor を触り始めたので、まずは公式チュートリアルを進めながら、
- このファイルは何をしているのか
- React でいうと何に近いのか
- 画面はどうやって切り替わるのか
を自分なりに整理してみました。
今回は主に、以下の観点で見ています。
-
Program.csは何をしているのか -
App.razorやRoutes.razorの役割 -
Pages配下の.razorファイルがどうルーティングされるのか -
@rendermode InteractiveServerは何を意味するのか
まず全体像
Blazor Web App では、.razor コンポーネントを中心に画面を構成します。
最初に触ってみて感じたのは、React の感覚で見ても対応関係を作りやすい ということでした。
ざっくり対応を並べるとこんな感じです。
| React | Blazor |
|---|---|
index.js + App コンポーネント |
App.razor |
BrowserRouter + Routes
|
Routes.razor |
<Route path="/" element={<Home />} /> |
@page "/" |
<Outlet /> に子ルートを表示 |
RouteView で対象コンポーネントを描画 |
| 共通レイアウトコンポーネント | MainLayout.razor |
もちろん完全に同じではありませんが、
「アプリ全体の入口」「ルーティング」「各ページコンポーネント」 という見方をすると理解しやすかったです。
Program.cs はアプリの起動地点
Program.cs は、Blazor Web App の起動設定を行う場所です。
React でいう「画面そのもの」ではなく、
どちらかというと アプリケーションの初期設定・サービス登録・起動処理 を担うエントリーポイントです。
ここで例えば、
- アプリを起動する
- 必要なサービスをDIコンテナに登録する
- Razor Components を有効化する
- Interactive Server などの実行モードを有効化する
といった設定を行います。
そのため、Program.cs は
フロントエンドのエントリーポイントというより、ASP.NET Core アプリの起動設定 と捉えたほうが自然でした。
App.razor はアプリ全体の土台
App.razor は、アプリ全体のHTMLの土台を持つファイルです。
チュートリアル直後の構成では、例えば以下のようになっています。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<base href="/" />
<link rel="stylesheet" href="@Assets["lib/bootstrap/dist/css/bootstrap.min.css"]" />
<link rel="stylesheet" href="@Assets["app.css"]" />
<link rel="stylesheet" href="@Assets["BlazorApp_Web_Test.styles.css"]" />
<ImportMap />
<link rel="icon" type="image/png" href="favicon.png" />
<HeadOutlet />
</head>
<body>
<Routes />
<script src="_framework/blazor.web.js"></script>
</body>
</html>
ここで重要なのは、<Routes /> が置かれていることです。
つまり App.razor は、
- HTMLのベースを定義し
- スタイルやスクリプトを読み込み
- 実際のページ切り替えは
Routesに委ねる
という役割を持っています。
Reactでいうと、index.html と App の役割が少し混ざったような印象を受けました。
Routes.razor がルーティングを担う
ルーティングの中心になるのが Routes.razor です。
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
このコードを見て理解したことは次の通りです。
Router
現在のURLに応じて、どのコンポーネントを表示するかを判断します。
RouteView
URLに対応するコンポーネントを描画します。
さらに DefaultLayout を指定することで、共通レイアウトの中に各ページを埋め込めます。
FocusOnNavigate
ページ遷移時に指定した要素へフォーカスを移します。
ここでは h1 にフォーカスするようになっており、アクセシビリティ向上のための仕組みです。
Pages 配下の .razor がページになる
Pages ディレクトリには、画面として扱われる .razor ファイルが置かれます。
例えば Counter.razor に以下のような記述があるとします。
@page "/counter"
@rendermode InteractiveServer
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
このとき /counter にアクセスすると、この Counter.razor が表示されます。
ポイントは、ルーティング定義を中央でまとめて書くというより、各ページ側で @page を宣言する ことです。
最初は少し不思議に感じましたが、
「このコンポーネントはこのURLを担当する」とページ自身が宣言している、と考えると分かりやすかったです。
@rendermode InteractiveServer とは何か
このコードで特に気になったのが @rendermode InteractiveServer でした。
@rendermode InteractiveServer
これは、そのコンポーネントを Interactive Server モード で動かす指定です。
このモードでは、UIイベントはサーバー側で処理され、
ブラウザとはリアルタイム接続を通じてやり取りされます。
たとえばボタンをクリックすると、
- ブラウザでクリックイベントが発生する
- サーバー側のコンポーネントにイベントが送られる
- サーバー側で状態が更新される
- 差分がブラウザに反映される
という流れで動きます。
そのため、見た目はSPAっぽく動きますが、
処理の主体はサーバー側にある のが特徴です。
@onclick は Blazor のイベントバインディング
この部分はかなり直感的でした。
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
React の onClick に近い感覚で読めます。
クリック時に IncrementCount メソッドが呼ばれ、
その中で currentCount を更新すると、画面が再描画されます。
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
「状態を変えたらUIに反映される」という意味では React とかなり似ていますが、
Blazor では C# のメソッドをそのままイベントハンドラとして書けるのが面白いところです。
親から子へ値を渡すには [Parameter]
Blazor コンポーネント間で値を渡すときは、[Parameter] 属性を使います。
子コンポーネント側はこんな形です。
@page "/counter"
@rendermode InteractiveServer
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
[Parameter]
public int IncrementAmount { get; set; } = 1;
private void IncrementCount()
{
currentCount += IncrementAmount;
}
}
親コンポーネント側はこんな形です。
@page "/"
<PageTitle>Home</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<br />
<br />
<Counter IncrementAmount="5" />
<br />
<br />
<Weather />
React の props に近い考え方ですが、
C# のプロパティとして受け取るのが特徴です。
開発中の実行は dotnet watch
開発中は dotnet watch を使うと変更を監視しながら実行できます。
React の npm start や Vite の開発サーバーの感覚に近く、
修正しながら動作確認しやすいです。
触ってみて感じたこと
Blazor Web App を最初に触ってみて、個人的には次のように整理できました。
-
Program.csはアプリ起動と設定 -
App.razorは全体の土台 -
Routes.razorがルーティングの中心 -
Pages配下の.razorが実際のページ -
@pageでURLを担当する -
@rendermodeで実行モードを切り替える -
@onclickや[Parameter]など、コンポーネント指向の書き味がある
React経験者として見ると、完全に同じではないものの、
「どこが対応しているか」を掴みながら進めると理解しやすい と感じました。
次は、InteractiveServer と InteractiveWebAssembly の違いや、
Blazor Web App と Blazor WebAssembly の関係も整理してみたいです。