Blazorアプリ-テンプレートを用いたプロジェクト

VisualStudioでテンプレートとしてBlazor WebAssemblyを選択してWebアプリを作ります。
これがBlazor WebAssemblyを用いたWebアプリを作るには標準的な方法となります。
コンソールアプリをテンプレートとした方法に比べて多くのファイルが自動的に作成されます。

プロジェクトの新規作成

VisualStudioより「新しいプロジェクトの作成」を選択します。
テンプレートより「Blazor WebAssembly アプリが空です」を選択します。
これはWebAssembly上で実行するBlazorアプリを作成するための空のプロジェクトテンプレートです。
プロジェクト名に「HelloBlazorApp」と入力します。
フレームワークとして「.Net 8.0」以上を選択してプロジェクトの作成を行います。
(但し、私の環境では何故か「.Net7.0」しか選べないので、後でプロジェクトファイルを修正します。)

プロジェクトファイルの変更

私の環境では何故かフレームワークとして「.Net7.0」しか選べないので、プロジェクトファイルを修正します。)
ソリューションエクスプローラーで「HelloBlazorApp」と表示されているのがプロジェクトファイルです。
これをダブルクリックして開くと以下のようなXML形式のファイルが表示されます。

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

  <PropertyGroup>
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="7.0.19" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="7.0.19" PrivateAssets="all" />
  </ItemGroup>

</Project>

.Net、WebAssemblyおよびWebAssembly.DevServerのバージョンが7.0になっているので、8.0用に書き換えます。
インストールされているバージョンをPowerShellのコマンド「dotnet --info」で調べ.Netのバージョンに合わせて変更します。
私の場合は次のように書き換えました。

<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.18" />
    <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.18" PrivateAssets="all" />
  </ItemGroup>

</Project>

プロジェクトファイルの変更を保存します。このときVisual Studioでプロジェクトの再読み込みを求められる場合は再読み込みを行います。

プロジェクトの実行

以上でプロジェクトができましたのでビルドして実行すると、以下のように画面がブラウザに表示されています。

ブラウザ

このように簡単な手続きでコンソールをテンプレートとした場合と同じようなアプリができました。
Webサーバーへの配置はサーバーへの配置をご覧ください。

コンソールプロジェクトの相違

コンソールプロジェクト(コンソールアプリ-テンプレートを用いたプロジェクト)とBlazorプロジェクト(Blazorアプリ-テンプレートを用いたプロジェクト)では幾つかの違いがあります。
2つのプロジェクトのソリューションエクスプローラは次の様になります。

構成ファイルの相違

プロジェクトに含まれるファイルの相違を見てみます。
2つのプロジェクトのソリューションエクスプローラは次のようになります。

コンソールプロジェクト Blazorプロジェクト
コンソールプロジェクト Blazorプロジェクト

Blazorプロジェクトにはコンソールプロジェクトに追加されたファイルと内容の異なるファイルがあります。

追加されたファイル

Blazorプロジェクトでは幾つかのフォルダとファイルが追加されています。

cssフォルダ

wwwrootフォルダ内にcssフォルダが作られています。
wwwrootフォルダはhtmlより参照するスタイルシートファイル配置する場所です。

app.css

wwwrootフォルダ内にapp..cssファイルが作られています。
これはindex.htmlから参照するスタイルシートです。

Pagesフォルダ

プロジェクト直下にPagesフォルダが作られています。
Pagesフォルダは、ルーティング可能なRazorコンポーネント(.razorファイル)を配置する場所です。
各コンポーネントは、@pageディレクティブで指定されたルートに対応します。
具体的には、以下の役割があります。

ルーティング可能なページの配置:

Pagesフォルダには、Webアプリケーションの各ページに対応するRazorコンポーネントを配置します。

ルートの指定:

各コンポーネントは、@pageディレクティブを使用して、対応するURLルートを指定します。

レイアウトの適用:

Pagesフォルダ内のコンポーネントは、レイアウトコンポーネント(例: MainLayout.razor)を適用できます。

フォルダ構成:

Pagesフォルダ内にさらにサブフォルダを作成することで、ページをグループ化し、アプリケーションの構造を整理できます。
例えば、Adminという名前のサブフォルダを作成し、その中に管理者向けのページを配置することができます。

index.razor

Pagesフォルダ内にindex.razorが作られています。
index.razor ファイルは、通常、アプリケーションのホームページを構成するRazorコンポーネントです。
これは次のような内容になっています。

@page "/"

<h1>Hello, world!</h1>

@page "/":

このディレクティブは、このコンポーネントがルートURL ("/")に対応することを指定します。

<h1>Hello, world!</h1>

ルートページのコンテンツです。
アプリケーションのホームページに表示されるHTMLやBlazorコンポーネントなどのコンテンツを定義します。
HTMLとC#コードを組み合わせたRazor構文を使用して、動的なコンテンツやロジックを記述します。

Imports.razor

Imports.razor ファイルは、Blazorアプリケーションにおいて、特定のフォルダとその配下のフォルダにあるRazorコンポーネントに共通の`@usingディレクティブや名前空間を適用するために使用される特別なファイルです。
このファイルに記述された内容は、同じフォルダおよびそのサブフォルダ内のすべての.razor`ファイルに自動的にインポートされます。
これは次のような内容になっています。

@using System.Net.Http
@using System.Net.Http.Json
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.JSInterop
@using HelloBlazorApp

App.razor

App.razorは、Blazorアプリケーションのルートコンポーネントを定義するファイルです。
Blazorアプリケーションの起動時に最初に読み込まれるコンポーネントであり、アプリ全体のレイアウトやルーティングなどを管理します。
これは次のような内容になっています。

<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <PageTitle>Not found</PageTitle>
        <LayoutView Layout="@typeof(MainLayout)">
            <p role="alert">Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

ここに書かれている  コンポーネントは、ブラウザの現在の URL に基づいて、対応する .razor ページを探して表示する役割を担っています。

AppAssembly="@typeof(Program).Assembly"

Blazor に「どのアセンブリからルート情報を探すか」を指定しています。通常、これは Program.cs に定義されているアプリ本体です。

<Found Context="routeData">

URL に一致するルートが見つかった場合、routeData を使って表示するコンポーネントを決定します。

<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />

routeData に従って適切な Razor コンポーネントをレンダリングします。指定がない限り、MainLayout をレイアウトとして使います。

<NotFound>

URL が既存のルートに一致しないときに表示される内容です。

MainLayout.razor

MainLayout.razor は、すべてのページで共通して使われるレイアウトテンプレートで、ナビゲーションバーやフッター、固定コンテンツなどを含めるページの共通デザインを提供する作るファイルです。
App.razor の RouteView で DefaultLayout に設定され、全ページに自動適用されます。| 
これは次のような内容になっています。

@inherits LayoutComponentBase

<main>
    @Body
</main>

@inherits LayoutComponentBase

Razor コンポーネントが特定のレイアウト機能を使えるようにするための宣言です。
このファイルが LayoutComponentBase を継承することで、レイアウト用の特殊な構文(例えば @Body)を使用できるようになります。

<main>@Body</main>

@Body は、実際に表示したい Razor ページ(Index.razor など)の内容が 差し込まれる場所です。

 はその周りの HTML 要素で、通常コンテンツ領域を示します。

内容の異なるファイル

index.html

これは次のような内容になっています。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <title>HelloBlazorApp</title>
    <base href="/" />
    <link href="css/app.css" rel="stylesheet" />
    
    <!-- If you add any scoped CSS files, uncomment the following to load them
    <link href="HelloBlazorApp.styles.css" rel="stylesheet" /> -->
</head>

<body>
    <div id="app">Loading...</div>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">🗙</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
</body>

</html>

幾つか記述が追加されています。

<html lang="en">

htmlの言語指定です。日本語の場合は lang="ja" と指定します。

<base href="/" />

相対 URL の基準を指定します。
これが設定されていることで、アプリ内のリンクやリソースパスが正しく解釈され、Blazor のルーティングが期待通りに動作するようになります。
サーバーに配置するとき、ルート以外に配置する場合に変更が必要かも。

<link href="css/app.css" rel="stylesheet" />

CSS(スタイルシート)を読み込むための命令です。

<div id="blazor-error-ui">

Blazor WebAssembly アプリにおける エラーメッセージ表示領域です。
JavaScript または WebAssembly の読み込み中に問題が起きた場合などに、エラーメッセージを表示するために使われます。

Program.cs

これは次のような内容になっています。

using HelloBlazorApp;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.RootComponents.Add<HeadOutlet>("head::after");

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

await builder.Build().RunAsync();

using Microsoft.AspNetCore.Components.Web;

ブラウザとの連携や Web イベント(クリック・キーボード入力など)に関する機能を使うための宣言です。

builder.RootComponents.Add<HeadOutlet>("head::after");

アプリで HTML の <head> 要素内に動的に要素を追加するための仕組みです。
これにより、JavaScript やメタタグ、タイトルなどを C# のコンポーネントから動的に変更できるようになります。
例えばページごとに異なる <title> を設定することができるようになります。

builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });

Blazorアプリケーションの依存性注入コンテナにHttpClientサービスを登録する処理です。
具体的には、ScopedライフサイクルでHttpClientインスタンスを生成し、必要に応じてDIコンテナから取得できるようにします。
BaseAddressは、HttpClientがリクエストを送信する際のベースURLを設定します。
builder.HostEnvironment.BaseAddressは、BlazorアプリケーションのベースURLを表します。

このプロジェクトでは不要かと思います。