Note
ホスト Blazor WebAssembly ソリューションは引き続きサポートされますが、プロジェクト テンプレートは削除され、.NET 8 以降ではサポートされなくなりました。 この記事は、参照のために .NET 7 までの目次に表示されますが、.NET 7 はサポートされなくなった Standard サポート期間 リリースであることに注意してください。
警告
このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。
この記事では、ホストされているRazor アプリBlazor WebAssemblyコンポーネント統合シナリオについて説明します。これには、サーバー上のRazor コンポーネントのプリレンダリングが含まれます。
重要
ASP.NET Core リリース間でのフレームワークの変更により、この記事のさまざまな手順セットが作成されました。 この記事のガイダンスを使用する前に、この記事の上部にあるドキュメント バージョン セレクターが、アプリに使用する ASP.NET Core のバージョンと一致していることを確認します。
プリレンダリングにより、検索エンジンがページ ランクの計算に使える最初の HTTP 応答の内容がレンダリングされることで、検索エンジンの最適化 (SEO) が向上します。
ソリューションの構成
プリレンダリングの構成
ホストされる Blazor WebAssembly アプリのプリレンダリングを設定するには:
ASP.NET Core アプリで Blazor WebAssembly をホストします。 スタンドアロンの Blazor WebAssembly アプリを ASP.NET Core ソリューションに追加することも、ホスト型オプションを使用して Blazor WebAssembly プロジェクトテンプレートから作成されたホスト型の Blazor WebAssembly アプリを使用することもできます。
- Visual Studio: 追加情報 ダイアログで、ASP.NET Core Hosted チェックボックスをオンにして、Blazor WebAssemblyアプリを作成します。 この記事の例では、ソリューションに
BlazorHostedという名前が付けられています。 - Visual Studio Code/.NET CLI コマンド シェル:
dotnet new blazorwasm -ho(-ho|--hostedオプションを使用)。-o|--output {LOCATION}オプションを使用してソリューションのフォルダーを作成し、ソリューションのプロジェクト名前空間を設定します。 この記事の例では、ソリューションにBlazorHostedという名前が付けられています (dotnet new blazorwasm -ho -o BlazorHosted)。
この記事の例では、ホステッド ソリューションの名前 (アセンブリ名) は
BlazorHostedです。 クライアント プロジェクトの名前空間はBlazorHosted.Clientで、サーバー プロジェクトの名前空間はBlazorHosted.Serverです。- Visual Studio: 追加情報 ダイアログで、ASP.NET Core Hosted チェックボックスをオンにして、Blazor WebAssemblyアプリを作成します。 この記事の例では、ソリューションに
ファイルを削除して
wwwroot/index.htmlBlazor WebAssemblyClientプロジェクトから取り除きます。Client プロジェクトで、 の次の行を
Program.csします。- builder.RootComponents.Add<App>("#app"); - builder.RootComponents.Add<HeadOutlet>("head::after");_Host.cshtmlファイルを Server プロジェクトのPagesフォルダーに追加します。 これらのファイルは、Visual Studio または .NET CLI のコマンド シェルで Blazor Server コマンドを使用してdotnet new blazorserver -o BlazorServerテンプレートから作成されたプロジェクトから取得できます (-o BlazorServerオプションを指定すると、プロジェクトのフォルダーが作成されます)。 Server プロジェクトのPagesフォルダーにファイルを配置した後、ファイルに次の変更を加えます。_Host.cshtmlファイルに次の変更を加えます。ファイルの先頭にある
Pages名前空間を更新し、 Server アプリのページの名前空間と一致させます。 次の例に示す{APP NAMESPACE}プレースホルダーは、_Host.cshtmlファイルを提供したドナー アプリのページの名前空間を表します。削除:
- @namespace {APP NAMESPACE}.Pages追加:
@namespace BlazorHosted.Server.Pagesファイルの先頭に、
@usingプロジェクトの Client ディレクティブを追加します。@using BlazorHosted.Clientスタイルシートのリンクを、WebAssembly プロジェクトのスタイルシートをポイントするように更新します。 次の例では、クライアント プロジェクトの名前空間は
BlazorHosted.Clientになります。{APP NAMESPACE}プレースホルダーは、_Host.cshtmlファイルを提供したドナー アプリの名前空間を表します。<component>コンポーネントのコンポーネント タグ ヘルパー (HeadOutletタグ) を、コンポーネントをプリレンダリングするように更新します。削除:
- <link href="css/site.css" rel="stylesheet" /> - <link href="{APP NAMESPACE}.styles.css" rel="stylesheet" /> - <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />追加:
<link href="css/app.css" rel="stylesheet" /> <link href="BlazorHosted.Client.styles.css" rel="stylesheet" /> <component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" />Note
ブートストラップ スタイルシート (
<link>) を要求するcss/bootstrap/bootstrap.min.css要素はそのままにしておきます。Blazor スクリプトのソースを、クライアント側の Blazor WebAssembly スクリプトを使用するように更新します。
削除:
- <script src="_framework/blazor.server.js"></script>追加:
<script src="_framework/blazor.webassembly.js"></script>render-modeの コンポーネントタグヘルパー を更新し、Appを使用してルート WebAssemblyPrerendered コンポーネントをプリレンダリングします。削除:
- <component type="typeof(App)" render-mode="ServerPrerendered" />追加:
<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />重要
プリレンダリングは認証エンドポイントではサポートされていません (
/authentication/パス セグメント)。 詳細については、「ASP.NET Core Blazor WebAssembly のその他のセキュリティ シナリオ」を参照してください。
Program.csプロジェクトの Server ファイルで、フォールバック エンドポイントをindex.htmlファイルから_Host.cshtmlページに変更します。削除:
- app.MapFallbackToFile("index.html");追加:
app.MapFallbackToPage("/_Host");Client プロジェクトと Server プロジェクトでプリレンダリング中に 1 つまたは複数の一般的サービスが使用される場合、両方のプロジェクトから呼び出せるメソッドにサービス登録を入れます。 詳細については、「ASP.NET Core Blazor の依存関係の挿入」を参照してください。
Server プロジェクトを実行します。 ホストされる Blazor WebAssembly アプリは、クライアントの Server プロジェクトによってプリレンダリングされ ます。
Razor コンポーネントをページまたはビューに埋め込むための構成
次のセクションと例では、RazorClient アプリからサーバー アプリのページまたはビューにBlazor WebAssembly コンポーネントを埋め込むには、追加の構成が必要です。
Server プロジェクトには 次のファイルとフォルダーが含まれている必要があります。
Razor ページ:
Pages/Shared/_Layout.cshtmlPages/Shared/_Layout.cshtml.cssPages/_ViewImports.cshtmlPages/_ViewStart.cshtml
MVC:
Views/Shared/_Layout.cshtmlViews/Shared/_Layout.cshtml.cssViews/_ViewImports.cshtmlViews/_ViewStart.cshtml
上記のファイルは、次の方法で ASP.NET Core プロジェクト テンプレートからアプリを生成することによって取得できます。
- Visual Studio の新しいプロジェクト作成ツール。
- コマンド シェルを開き、
dotnet new webapp -o {PROJECT NAME}(Razor Pages) またはdotnet new mvc -o {PROJECT NAME}(MVC) を実行する。-o|--outputプレースホルダーの値を持つ{PROJECT NAME}オプションは、アプリの名前を指定して、アプリのフォルダーを作成します。
インポートされた _ViewImports.cshtml ファイル内の名前空間を、ファイルを受け取る Server プロジェクトで使用されているものと一致するように更新します。
Pages/_ViewImports.cshtml (Razor ページ):
@using BlazorHosted.Server
@namespace BlazorHosted.Server.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Views/_ViewImports.cshtml (MVC):
@using BlazorHosted.Server
@using BlazorHosted.Server.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
インポートされたレイアウト ファイルを更新します。これは、Pages/Shared/_Layout.cshtml Pages の場合は Razor、MVC の場合は Views/Shared/_Layout.cshtml です。
最初に、ドナー プロジェクトからタイトルとスタイルシートを削除します。これは、次の例では RPDonor.styles.css です。
{PROJECT NAME} プレースホルダーは、ドナー プロジェクトのアプリ名を表します。
- <title>@ViewData["Title"] - {PROJECT NAME}</title>
- <link rel="stylesheet" href="~/RPDonor.styles.css" asp-append-version="true" />
レイアウト ファイルに Client プロジェクトのスタイルを含めます。 次の例では、 Client プロジェクトの名前空間は BlazorHosted.Client になります。
<title> 要素は同時に更新できます。
レイアウト ファイルの <head> の内容に次の行を配置します。
<title>@ViewData["Title"] - BlazorHosted</title>
<link href="css/app.css" rel="stylesheet" />
<link rel="stylesheet" href="BlazorHosted.Client.styles.css" asp-append-version="true" />
<component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" />
インポートされたレイアウトには 2 つの HomeIndex ページと Privacy のナビゲーション リンクが含まれます。
Home リンクが、ホストされる Blazor WebAssembly アプリを指すようにするには、ハイパーリンクを次のように変更します。
- <a class="navbar-brand" asp-area="" asp-page="/Index">{PROJECT NAME}</a>
+ <a class="navbar-brand" href="/">BlazorHosted</a>
- <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>
MVC レイアウト ファイルの場合:
- <a class="navbar-brand" asp-area="" asp-controller="Home"
- asp-action="Index">{PROJECT NAME}</a>
+ <a class="navbar-brand" href="/">BlazorHosted</a>
- <a class="nav-link text-dark" asp-area="" asp-controller="Home"
- asp-action="Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>
<footer> 要素のアプリ名を更新します。 次の例では、BlazorHosted というアプリ名を使用しています。
- © {DATE} - {DONOR NAME} - <a asp-area="" asp-page="/Privacy">Privacy</a>
+ © {DATE} - BlazorHosted - <a asp-area="" asp-page="/Privacy">Privacy</a>
前の例では、{DATE} プレースホルダーは Razor Pages または MVC プロジェクト テンプレートから生成されたアプリの著作権日を表します。
Privacy リンクをプライバシー ページ (Razor ページ) に移動するには、Server プロジェクトにプライバシー ページを追加します。
Pages/Privacy.cshtml プロジェクトの Server:
@page
@model PrivacyModel
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>
MVC ベースのプライバシー ビューの場合は、Server プロジェクトでプライバシー ビューを作成します。
View/Home/Privacy.cshtml プロジェクトの Server:
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>
MVC アプリの Home コントローラーで、ビューを返します。
Controllers/HomeController.cs に次のコードを追加します。
public IActionResult Privacy()
{
return View();
}
ドナー アプリからファイルをインポートする場合は、ファイル内のすべての名前空間を更新して、 Server プロジェクトの名前空間 (たとえば、BlazorHosted.Server) に一致させてください。
ドナー プロジェクトの Server フォルダーから wwwroot プロジェクトに静的アセットをインポートします。
-
wwwroot/cssフォルダーと内容 -
wwwroot/jsフォルダーと内容 -
wwwroot/libフォルダーと内容
ドナー プロジェクトが ASP.NET Core プロジェクト テンプレートから作成され、ファイルが変更されていない場合は、ドナー プロジェクトの wwwroot フォルダー全体を Server プロジェクトにコピー して、favicon アイコン ファイルを削除することができます。
警告
静的アセットを Client フォルダーと Serverwwwroot フォルダーの両方に配置するのは避けてください。 両方のフォルダーに同じファイルがある場合は、各静的アセットで同じ Web ルート パスが共有されるため、例外がスローされます。 そのため、静的アセットを wwwroot フォルダーのいずれか (両方ではなく) でホストします。
上記の構成を採用した後で、Razor コンポーネントを Server プロジェクトのページまたはビューに埋め込みます。 この記事の以降のセクションのガイダンスを使用してください。
- コンポーネント タグ ヘルパーを使用してページまたはビューのコンポーネントをレンダリングする
- CSS セレクターを使用してページまたはビューのコンポーネントをレンダリングする
コンポーネント タグ ヘルパーを使用してページまたはビューのコンポーネントをレンダリングする
追加構成を含むソリューションを構成した後、コンポーネント タグ ヘルパーは、Blazor WebAssembly アプリのコンポーネントをページまたはビュー内にレンダリングするための 2 つのレンダー モードをサポートします。
次の Razor Pages の例では、Counter コンポーネントがページにレンダリングされます。 コンポーネントを対話形式にするために、ページのBlazor WebAssemblyに スクリプトが含まれます。
Counter コンポーネントで コンポーネントタグヘルパー ({ASSEMBLY NAME}.Pages.Counter) の完全な名前空間を使用しないようにするために、クライアントプロジェクトの @using 名前空間に Pages ディレクティブを追加します。 次の例では、 Client プロジェクトの名前空間は BlazorHosted.Client になります。
Server プロジェクトの Pages/RazorPagesCounter1.cshtml:
@page
@using BlazorHosted.Client.Pages
<component type="typeof(Counter)" render-mode="WebAssemblyPrerendered" />
@section Scripts {
<script src="_framework/blazor.webassembly.js"></script>
}
Server プロジェクトを実行します。
Razor の /razorpagescounter1 ページに移動します。 プリレンダリングされた Counter コンポーネントはページに埋め込まれています。
RenderMode によって、コンポーネントに対して以下の構成が行われます。
- ページに事前レンダリングするかどうか。
- ページに静的 HTML としてレンダリングするかどうか。または、ユーザー エージェントから Blazor アプリをブートストラップするために必要な情報が含まれているかどうか。
パラメーターの渡し方や RenderMode の構成などのコンポーネント タグ ヘルパーの詳細については、「ASP.NET Core のコンポーネント タグ ヘルパー」を参照してください。
コンポーネントによって使用される静的リソースや、アプリでのレイアウト ページの整理方法によっては、追加の作業が必要になる場合があります。 通常、ページまたはビューの Scripts レンダー セクションにスクリプトが追加され、スタイル シートはレイアウトの <head> 要素コンテンツに追加されます。
レンダリング フラグメントを使用して子コンテンツを設定する
コンポーネント タグ ヘルパーでは、子コンテンツの RenderFragment デリゲート (param-ChildContent="..." など) の受信をサポートしていません。 渡される子コンテンツを使用してレンダリングするコンポーネントを参照する Razor コンポーネント (.razor) を作成し、ページまたはビューから Razor コンポーネントを呼び出すことをお勧めします。
最上位のプリレンダリングされたコンポーネントが発行時にトリミングされないようにする
コンポーネント タグ ヘルパーが、発行時にトリミングの対象となるライブラリからコンポーネントを直接参照している場合は、クライアント側のアプリ コードからの参照がないため、発行時にコンポーネントがトリミングされる可能性があります。 その結果、コンポーネントはプリレンダリングされず、出力に空白が残ります。 この問題が発生した場合は、クライアント側アプリの任意のクラスに DynamicDependency 属性を追加して、ライブラリ コンポーネントを保持するようにトリマーに指示します。
SomeLibraryComponentToBePreserved というコンポーネントを保持するには、任意のコンポーネントに次を追加します。
@using System.Diagnostics.CodeAnalysis
@attribute [DynamicDependency(DynamicallyAccessedMemberTypes.All,
typeof(SomeLibraryComponentToBePreserved))]
通常、アプリでは (トリミングされていない) コンポーネントをプリレンダリングするため、上記の方法は通常、必要ありません。これにより、ライブラリからコンポーネントが参照されます (これにより、トリミングもされません)。 ライブラリをトリミングするときにライブラリ コンポーネントを直接プリレンダリングする場合にのみ、DynamicDependency を明示的に使用します。
CSS セレクターを使用してページまたはビューのコンポーネントをレンダリングする
追加の構成を含めてソリューションを構成した後、Client ファイルでホストされる Blazor WebAssembly ソリューションの Program.cs プロジェクトにルート コンポーネントを追加します。 次の例では、Counter と一致する id を持つ要素を選択する CSS セレクターを使用して、ルート コンポーネントとして counter-component コンポーネントが宣言されています。 次の例では、 Client プロジェクトの名前空間は BlazorHosted.Client になります。
Program.cs プロジェクトの Client ファイルで、プロジェクトの Razor コンポーネントの名前空間をファイルの先頭に追加します。
using BlazorHosted.Client.Pages;
builder で Program.cs が確立されたら、Counter コンポーネントをルート コンポーネントとして追加します。
builder.RootComponents.Add<Counter>("#counter-component");
次の Razor Pages の例では、Counter コンポーネントがページにレンダリングされます。 コンポーネントを対話形式にするために、ページのBlazor WebAssemblyに スクリプトが含まれます。
Server プロジェクトの Pages/RazorPagesCounter2.cshtml:
@page
<div id="counter-component">Loading...</div>
@section Scripts {
<script src="_framework/blazor.webassembly.js"></script>
}
Server プロジェクトを実行します。
Razor の /razorpagescounter2 ページに移動します。 プリレンダリングされた Counter コンポーネントはページに埋め込まれています。
コンポーネントによって使用される静的リソースや、アプリでのレイアウト ページの整理方法によっては、追加の作業が必要になる場合があります。 通常、ページまたはビューの Scripts レンダー セクションにスクリプトが追加され、スタイル シートはレイアウトの <head> 要素コンテンツに追加されます。
Note
前の例では、JSException アプリが CSS セレクターを使用して Blazor WebAssembly Pages または MVC アプリにRazorプリレンダリングおよび統合された場合に がスローされます。 Client プロジェクトのいずれかの Razor コンポーネントに移動するか、埋め込みコンポーネントを含む Server のページまたはビューに移動すると、1 つ以上の JSException がスローされます。
ルーティング可能な Blazor WebAssembly コンポーネントを使用した Razor アプリのプリレンダリングおよび統合は、CSS セレクターの使用と両立しないため、これは通常の動作です。
前のセクションの例を使用している状態で、サンプル アプリで CSS セレクターが動作することを確認するだけの場合は、 App プロジェクトの Client ファイルの Program.cs ルート コンポーネントの仕様をコメント アウトしてください。
- builder.RootComponents.Add<App>("#app");
+ //builder.RootComponents.Add<App>("#app");
CSS セレクターを使用する埋め込みの Razor コンポーネント (たとえば、前の例の /razorpagescounter2) を含むページまたはビューに移動します。 ページまたはビューが埋め込みコンポーネントと共に読み込まれ、埋め込みコンポーネントは想定どおりに機能します。
プリレンダリングされた状態を保持する
プリレンダリングされた状態を保持しないと、プリレンダリング中に使用された状態は失われ、アプリが完全に読み込まれたときに再作成する必要があります。 いずれかの状態が非同期でセットアップされている場合、プリレンダリングされた UI は一時的なプレースホルダーに置き換えられてから再度完全にレンダリングされるため、UI がちらつくことがあります。
プリレンダリング済みコンポーネントの状態を保持するには、コンポーネントの状態保持タグ ヘルパー (参照ソース) を使用します。 コンポーネントをプリレンダリングするアプリの <persist-component-state /> ページの </body> の終了タグの内側に、タグ ヘルパのタグ _Host を追加します。
Note
通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。
ホストされた Pages/_Host.cshtml アプリ内の WebAssembly 事前レンダリング (Blazor) された WebAssemblyPrerendered アプリの Blazor WebAssembly では次のようになります。
<body>
...
<persist-component-state />
</body>
PersistentComponentState サービスを使用してどの状態を永続化するか決定します。 アプリが一時停止される前に、PersistentComponentState.RegisterOnPersisting によってコールバックが登録され、コンポーネントの状態が保持されます。 状態は、アプリケーションの再開時に取得されます。 アプリのシャットダウン中に競合状態になる可能性を回避するために、初期化コードの最後に呼び出しを行います。
次に例を示します。
-
{TYPE}プレースホルダーは、(WeatherForecast[]など) 永続化するデータの種類を表します。 -
{TOKEN}プレースホルダーは、(fetchdataなど) 状態識別子の文字列です。
@implements IDisposable
@inject PersistentComponentState ApplicationState
...
@code {
private {TYPE} data;
private PersistingComponentStateSubscription persistingSubscription;
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<{TYPE}>(
"{TOKEN}", out var restored))
{
data = await ...;
}
else
{
data = restored!;
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistData);
}
private Task PersistData()
{
ApplicationState.PersistAsJson("{TOKEN}", data);
return Task.CompletedTask;
}
void IDisposable.Dispose()
{
persistingSubscription.Dispose();
}
}
次の例は、FetchData プロジェクト テンプレートに基づいた、ホストされている Blazor WebAssembly アプリ内の Blazor コンポーネントの更新バージョンです。
WeatherForecastPreserveState コンポーネントは、プリレンダリング中に天気予報の状態を保持し、その後、コンポーネントを初期化するために状態を取得します。
永続コンポーネントの状態タグ ヘルパーでは、すべてのコンポーネントの呼び出しの後で、コンポーネントの状態を保持します。
Pages/WeatherForecastPreserveState.razor:
@page "/weather-forecast-preserve-state"
@using BlazorSample.Shared
@implements IDisposable
@inject IWeatherForecastService WeatherForecastService
@inject PersistentComponentState ApplicationState
<PageTitle>Weather Forecast</PageTitle>
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from the server.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[] forecasts = Array.Empty<WeatherForecast>();
private PersistingComponentStateSubscription persistingSubscription;
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<WeatherForecast[]>(
nameof(forecasts), out var restored))
{
forecasts = await WeatherForecastService.GetForecastAsync(
DateOnly.FromDateTime(DateTime.Now));
}
else
{
forecasts = restored!;
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistData);
}
private Task PersistData()
{
ApplicationState.PersistAsJson(nameof(forecasts), forecasts);
return Task.CompletedTask;
}
void IDisposable.Dispose()
{
persistingSubscription.Dispose();
}
}
プリレンダリング中に使用されたのと同じ状態でコンポーネントを初期化することにより、負荷の高い初期化ステップが 1 回だけ実行されます。 レンダリングされた UI もプリレンダリングされた UI に一致するので、ブラウザーでちらつきは発生しません。
永続化されたプリレンダリングされた状態はクライアントに転送され、そこでコンポーネントの状態を復元するために使用されます。 ホストされている Blazor WebAssembly アプリでプリレンダリングを行う場合、データはブラウザーに公開され、機密情報を含む必要はありません。
Blazor WebAssembly のその他のリソース
- アセンブリ遅延読み込みによるプリレンダリングのサポート
- プリレンダリングに関連する Razor コンポーネント ライフサイクルのトピック
-
コンポーネントの初期化 (
OnInitialized{Async}) -
コンポーネントのレンダリング後 (
OnAfterRender{Async}) - プリレンダリング後のステートフル再接続: このセクションの内容では、Blazor Server およびステートフルな SignalR の "再接続" に焦点を当てていますが、ホストされた アプリ (Blazor WebAssembly) でのプリレンダリングのシナリオでは、開発者コードを 2 回実行しないようにするための同様の条件とアプローチが必要です。WebAssemblyPrerendered プリレンダリング中に初期化コードの実行中に状態を保持するには、この記事の「プリレンダリングされた状態を保持する」セクションを参照してください。
- JavaScript 相互運用を使用したプリレンダリング
-
コンポーネントの初期化 (
- プリレンダリングに関連する認証と認可の話題
- ASP.NET Core のホストと展開Blazor WebAssembly
- エラーを処理する: プリレンダリング
-
OnNavigateAsync は、プリレンダリング時に 2 回実行されます。
OnNavigateAsyncで非同期ナビゲーション イベントを処理する
プリレンダリングにより、検索エンジンがページ ランクの計算に使える最初の HTTP 応答の内容がレンダリングされることで、検索エンジンの最適化 (SEO) が向上します。
ソリューションの構成
プリレンダリングの構成
ホストされる Blazor WebAssembly アプリのプリレンダリングを設定するには:
ASP.NET Core アプリで Blazor WebAssembly をホストします。 スタンドアロンの Blazor WebAssembly アプリを ASP.NET Core ソリューションに追加することも、ホスト型オプションを使用して Blazor WebAssembly プロジェクトテンプレートから作成されたホスト型の Blazor WebAssembly アプリを使用することもできます。
- Visual Studio: 追加情報 ダイアログで、ASP.NET Core Hosted チェックボックスをオンにして、Blazor WebAssemblyアプリを作成します。 この記事の例では、ソリューションに
BlazorHostedという名前が付けられています。 - Visual Studio Code/.NET CLI コマンド シェル:
dotnet new blazorwasm -ho(-ho|--hostedオプションを使用)。-o|--output {LOCATION}オプションを使用してソリューションのフォルダーを作成し、ソリューションのプロジェクト名前空間を設定します。 この記事の例では、ソリューションにBlazorHostedという名前が付けられています (dotnet new blazorwasm -ho -o BlazorHosted)。
この記事の例では、クライアント プロジェクトの名前空間は
BlazorHosted.Clientで、サーバー プロジェクトの名前空間はBlazorHosted.Serverです。- Visual Studio: 追加情報 ダイアログで、ASP.NET Core Hosted チェックボックスをオンにして、Blazor WebAssemblyアプリを作成します。 この記事の例では、ソリューションに
ファイルを削除して
wwwroot/index.htmlBlazor WebAssemblyClientプロジェクトから取り除きます。Client プロジェクトで、 の次の行を
Program.csします。- builder.RootComponents.Add<App>("#app"); - builder.RootComponents.Add<HeadOutlet>("head::after");_Host.cshtmlファイルと_Layout.cshtmlファイルを Server プロジェクトのPagesフォルダーに追加します。 これらのファイルは、Visual Studio または .NET CLI のコマンド シェルで Blazor Server コマンドを使用してdotnet new blazorserver -o BlazorServerテンプレートから作成されたプロジェクトから取得できます (-o BlazorServerオプションを指定すると、プロジェクトのフォルダーが作成されます)。 Server プロジェクトのPagesフォルダーにファイルを配置した後、ファイルに次の変更を加えます。重要
_Layout.cshtmlコンポーネントのコンテンツ(例えば、ページのタイトルは コンポーネント、その他のhead要素は HeadOutlet コンポーネント)を制御するには、_Layout.cshtmlコンポーネント用のPageTitleを使ったレイアウトページ (HeadContent) の使用が必須です。 詳しくは、「ASP.NET Core Blazor アプリで コンテンツを制御する」をご覧ください。_Layout.cshtmlファイルに次の変更を加えます。ファイルの先頭にある
Pages名前空間を更新し、 Server アプリのページの名前空間と一致させます。 次の例に示す{APP NAMESPACE}プレースホルダーは、_Layout.cshtmlファイルを提供したドナー アプリのページの名前空間を表します。削除:
- @namespace {APP NAMESPACE}.Pages追加:
@namespace BlazorHosted.Server.Pagesファイルの先頭に、
@usingプロジェクトの Client ディレクティブを追加します。@using BlazorHosted.Clientスタイルシートのリンクを、WebAssembly プロジェクトのスタイルシートをポイントするように更新します。 次の例では、クライアント プロジェクトの名前空間は
BlazorHosted.Clientになります。{APP NAMESPACE}プレースホルダーは、_Layout.cshtmlファイルを提供したドナー アプリの名前空間を表します。<component>コンポーネントのコンポーネント タグ ヘルパー (HeadOutletタグ) を、コンポーネントをプリレンダリングするように更新します。削除:
- <link href="css/site.css" rel="stylesheet" /> - <link href="{APP NAMESPACE}.styles.css" rel="stylesheet" /> - <component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />追加:
<link href="css/app.css" rel="stylesheet" /> <link href="BlazorHosted.Client.styles.css" rel="stylesheet" /> <component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" />Note
ブートストラップ スタイルシート (
<link>) を要求するcss/bootstrap/bootstrap.min.css要素はそのままにしておきます。Blazor スクリプトのソースを、クライアント側の Blazor WebAssembly スクリプトを使用するように更新します。
削除:
- <script src="_framework/blazor.server.js"></script>追加:
<script src="_framework/blazor.webassembly.js"></script>
_Host.cshtmlファイルで次の操作を行います。Pages名前空間を Client プロジェクトの名前空間に変更します。{APP NAMESPACE}プレースホルダーは、_Host.cshtmlファイルを提供したドナー アプリのページの名前空間を表します。削除:
- @namespace {APP NAMESPACE}.Pages追加:
@namespace BlazorHosted.Clientrender-modeの コンポーネントタグヘルパー を更新し、Appを使用してルート WebAssemblyPrerendered コンポーネントをプリレンダリングします。削除:
- <component type="typeof(App)" render-mode="ServerPrerendered" />追加:
<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />重要
プリレンダリングは認証エンドポイントではサポートされていません (
/authentication/パス セグメント)。 詳細については、「ASP.NET Core Blazor WebAssembly のその他のセキュリティ シナリオ」を参照してください。
Server 内の
Program.csプロジェクトのエンドポイント マッピングで、フォールバックをindex.htmlファイルから_Host.cshtmlページに変更します。削除:
- app.MapFallbackToFile("index.html");追加:
app.MapFallbackToPage("/_Host");Client プロジェクトと Server プロジェクトでプリレンダリング中に 1 つまたは複数の一般的サービスが使用される場合、両方のプロジェクトから呼び出せるメソッドにサービス登録を入れます。 詳細については、「ASP.NET Core Blazor の依存関係の挿入」を参照してください。
Server プロジェクトを実行します。 ホストされる Blazor WebAssembly アプリは、クライアントの Server プロジェクトによってプリレンダリングされ ます。
Razor コンポーネントをページまたはビューに埋め込むための構成
次のセクションと例では、RazorClient アプリからサーバー アプリのページまたはビューにBlazor WebAssembly コンポーネントを埋め込むには、追加の構成が必要です。
Server プロジェクトには 次のファイルとフォルダーが含まれている必要があります。
Razor ページ:
Pages/Shared/_Layout.cshtmlPages/Shared/_Layout.cshtml.cssPages/_ViewImports.cshtmlPages/_ViewStart.cshtml
MVC:
Views/Shared/_Layout.cshtmlViews/Shared/_Layout.cshtml.cssViews/_ViewImports.cshtmlViews/_ViewStart.cshtml
重要
_Layout.cshtml コンポーネントのコンテンツ(例えば、ページのタイトルは コンポーネント、その他のhead要素は HeadOutlet コンポーネント)を制御するには、_Layout.cshtml コンポーネント用のPageTitleを使ったレイアウトページ (HeadContent) の使用が必須です。 詳しくは、「ASP.NET Core Blazor アプリで コンテンツを制御する」をご覧ください。
上記のファイルは、次の方法で ASP.NET Core プロジェクト テンプレートからアプリを生成することによって取得できます。
- Visual Studio の新しいプロジェクト作成ツール。
- コマンド シェルを開き、
dotnet new webapp -o {PROJECT NAME}(Razor Pages) またはdotnet new mvc -o {PROJECT NAME}(MVC) を実行する。-o|--outputプレースホルダーの値を持つ{PROJECT NAME}オプションは、アプリの名前を指定して、アプリのフォルダーを作成します。
インポートされた _ViewImports.cshtml ファイル内の名前空間を、ファイルを受け取る Server プロジェクトで使用されているものと一致するように更新します。
Pages/_ViewImports.cshtml (Razor ページ):
@using BlazorHosted.Server
@namespace BlazorHosted.Server.Pages
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Views/_ViewImports.cshtml (MVC):
@using BlazorHosted.Server
@using BlazorHosted.Server.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
インポートされたレイアウト ファイルを更新します。これは、Pages/Shared/_Layout.cshtml Pages の場合は Razor、MVC の場合は Views/Shared/_Layout.cshtml です。
最初に、ドナー プロジェクトからタイトルとスタイルシートを削除します。これは、次の例では RPDonor.styles.css です。
{PROJECT NAME} プレースホルダーは、ドナー プロジェクトのアプリ名を表します。
- <title>@ViewData["Title"] - {PROJECT NAME}</title>
- <link rel="stylesheet" href="~/RPDonor.styles.css" asp-append-version="true" />
レイアウト ファイルに Client プロジェクトのスタイルを含めます。 次の例では、 Client プロジェクトの名前空間は BlazorHosted.Client になります。
<title> 要素は同時に更新できます。
レイアウト ファイルの <head> の内容に次の行を配置します。
<title>@ViewData["Title"] - BlazorHosted</title>
<link href="css/app.css" rel="stylesheet" />
<link rel="stylesheet" href="BlazorHosted.Client.styles.css" asp-append-version="true" />
<component type="typeof(HeadOutlet)" render-mode="WebAssemblyPrerendered" />
インポートされたレイアウトには 2 つの HomeIndex ページと Privacy のナビゲーション リンクが含まれます。
Home リンクが、ホストされる Blazor WebAssembly アプリを指すようにするには、ハイパーリンクを次のように変更します。
- <a class="navbar-brand" asp-area="" asp-page="/Index">{PROJECT NAME}</a>
+ <a class="navbar-brand" href="/">BlazorHosted</a>
- <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>
MVC レイアウト ファイルの場合:
- <a class="navbar-brand" asp-area="" asp-controller="Home"
- asp-action="Index">{PROJECT NAME}</a>
+ <a class="navbar-brand" href="/">BlazorHosted</a>
- <a class="nav-link text-dark" asp-area="" asp-controller="Home"
- asp-action="Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>
<footer> 要素のアプリ名を更新します。 次の例では、BlazorHosted というアプリ名を使用しています。
- © {DATE} - {DONOR NAME} - <a asp-area="" asp-page="/Privacy">Privacy</a>
+ © {DATE} - BlazorHosted - <a asp-area="" asp-page="/Privacy">Privacy</a>
前の例では、{DATE} プレースホルダーは Razor Pages または MVC プロジェクト テンプレートから生成されたアプリの著作権日を表します。
Privacy リンクをプライバシー ページ (Razor ページ) に移動するには、Server プロジェクトにプライバシー ページを追加します。
Pages/Privacy.cshtml プロジェクトの Server:
@page
@model PrivacyModel
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>
MVC ベースのプライバシー ビューの場合は、Server プロジェクトでプライバシー ビューを作成します。
View/Home/Privacy.cshtml プロジェクトの Server:
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<p>Use this page to detail your site's privacy policy.</p>
MVC アプリの Home コントローラーで、ビューを返します。
Controllers/HomeController.cs に次のコードを追加します。
public IActionResult Privacy()
{
return View();
}
ドナー アプリからファイルをインポートする場合は、ファイル内のすべての名前空間を更新して、 Server プロジェクトの名前空間 (たとえば、BlazorHosted.Server) に一致させてください。
ドナー プロジェクトの Server フォルダーから wwwroot プロジェクトに静的アセットをインポートします。
-
wwwroot/cssフォルダーと内容 -
wwwroot/jsフォルダーと内容 -
wwwroot/libフォルダーと内容
ドナー プロジェクトが ASP.NET Core プロジェクト テンプレートから作成され、ファイルが変更されていない場合は、ドナー プロジェクトの wwwroot フォルダー全体を Server プロジェクトにコピー して、favicon アイコン ファイルを削除することができます。
警告
静的アセットを Client フォルダーと Serverwwwroot フォルダーの両方に配置するのは避けてください。 両方のフォルダーに同じファイルがある場合は、各フォルダー内の静的アセットが同じ Web ルート パスを共有しているため、例外がスローされます。 そのため、静的アセットを両方ではなく、いずれかの wwwroot フォルダーにホストします。
上記の構成を採用した後で、Razor コンポーネントを Server プロジェクトのページまたはビューに埋め込みます。 この記事の以降のセクションのガイダンスを使用してください。
- コンポーネント タグ ヘルパーを使用してページまたはビューのコンポーネントをレンダリングする
- CSS セレクターを使用してページまたはビューのコンポーネントをレンダリングする
コンポーネント タグ ヘルパーを使用してページまたはビューのコンポーネントをレンダリングする
追加構成を含むソリューションを構成した後、コンポーネント タグ ヘルパーは、Blazor WebAssembly アプリのコンポーネントをページまたはビュー内にレンダリングするための 2 つのレンダー モードをサポートします。
次の Razor Pages の例では、Counter コンポーネントがページにレンダリングされます。 コンポーネントを対話形式にするために、ページのBlazor WebAssemblyに スクリプトが含まれます。
Counter コンポーネントで コンポーネントタグヘルパー ({ASSEMBLY NAME}.Pages.Counter) の完全な名前空間を使用しないようにするために、クライアントプロジェクトの @using 名前空間に Pages ディレクティブを追加します。 次の例では、 Client プロジェクトの名前空間は BlazorHosted.Client になります。
Server プロジェクトの Pages/RazorPagesCounter1.cshtml:
@page
@using BlazorHosted.Client.Pages
<component type="typeof(Counter)" render-mode="WebAssemblyPrerendered" />
@section Scripts {
<script src="_framework/blazor.webassembly.js"></script>
}
Server プロジェクトを実行します。
Razor の /razorpagescounter1 ページに移動します。 プリレンダリングされた Counter コンポーネントはページに埋め込まれています。
RenderMode によって、コンポーネントに対して以下の構成が行われます。
- ページに事前レンダリングするかどうか。
- ページに静的 HTML としてレンダリングするかどうか。または、ユーザー エージェントから Blazor アプリをブートストラップするために必要な情報が含まれているかどうか。
パラメーターの渡し方や RenderMode の構成などのコンポーネント タグ ヘルパーの詳細については、「ASP.NET Core のコンポーネント タグ ヘルパー」を参照してください。
コンポーネントによって使用される静的リソースや、アプリでのレイアウト ページの整理方法によっては、追加の作業が必要になる場合があります。 通常、ページまたはビューの Scripts レンダー セクションにスクリプトが追加され、スタイル シートはレイアウトの <head> 要素コンテンツに追加されます。
レンダリング フラグメントを使用して子コンテンツを設定する
コンポーネント タグ ヘルパーでは、子コンテンツの RenderFragment デリゲート (param-ChildContent="..." など) の受信をサポートしていません。 渡される子コンテンツを使用してレンダリングするコンポーネントを参照する Razor コンポーネント (.razor) を作成し、ページまたはビューから Razor コンポーネントを呼び出すことをお勧めします。
最上位のプリレンダリングされたコンポーネントが発行時にトリミングされないようにする
コンポーネント タグ ヘルパーが、発行時にトリミングの対象となるライブラリからコンポーネントを直接参照している場合は、クライアント側のアプリ コードからの参照がないため、発行時にコンポーネントがトリミングされる可能性があります。 その結果、コンポーネントはプリレンダリングされず、出力に空白が残ります。 この問題が発生した場合は、クライアント側アプリの任意のクラスに DynamicDependency 属性を追加して、ライブラリ コンポーネントを保持するようにトリマーに指示します。
SomeLibraryComponentToBePreserved というコンポーネントを保持するには、任意のコンポーネントに次を追加します。
@using System.Diagnostics.CodeAnalysis
@attribute [DynamicDependency(DynamicallyAccessedMemberTypes.All,
typeof(SomeLibraryComponentToBePreserved))]
通常、アプリでは (トリミングされていない) コンポーネントをプリレンダリングするため、上記の方法は通常、必要ありません。これにより、ライブラリからコンポーネントが参照されます (これにより、トリミングもされません)。 ライブラリをトリミングするときにライブラリ コンポーネントを直接プリレンダリングする場合にのみ、DynamicDependency を明示的に使用します。
CSS セレクターを使用してページまたはビューのコンポーネントをレンダリングする
追加の構成を含めてソリューションを構成した後、Client ファイルでホストされる Blazor WebAssembly ソリューションの Program.cs プロジェクトにルート コンポーネントを追加します。 次の例では、Counter と一致する id を持つ要素を選択する CSS セレクターを使用して、ルート コンポーネントとして counter-component コンポーネントが宣言されています。 次の例では、 Client プロジェクトの名前空間は BlazorHosted.Client になります。
Program.cs プロジェクトの Client ファイルで、プロジェクトの Razor コンポーネントの名前空間をファイルの先頭に追加します。
using BlazorHosted.Client.Pages;
builder で Program.cs が確立されたら、Counter コンポーネントをルート コンポーネントとして追加します。
builder.RootComponents.Add<Counter>("#counter-component");
次の Razor Pages の例では、Counter コンポーネントがページにレンダリングされます。 コンポーネントを対話形式にするために、ページのBlazor WebAssemblyに スクリプトが含まれます。
Server プロジェクトの Pages/RazorPagesCounter2.cshtml:
@page
<div id="counter-component">Loading...</div>
@section Scripts {
<script src="_framework/blazor.webassembly.js"></script>
}
Server プロジェクトを実行します。
Razor の /razorpagescounter2 ページに移動します。 プリレンダリングされた Counter コンポーネントはページに埋め込まれています。
コンポーネントによって使用される静的リソースや、アプリでのレイアウト ページの整理方法によっては、追加の作業が必要になる場合があります。 通常、ページまたはビューの Scripts レンダー セクションにスクリプトが追加され、スタイル シートはレイアウトの <head> 要素コンテンツに追加されます。
Note
前の例では、JSException アプリが CSS セレクターを使用して Blazor WebAssembly Pages または MVC アプリにRazorプリレンダリングおよび統合された場合に がスローされます。 Client プロジェクトのいずれかの Razor コンポーネントに移動するか、埋め込みコンポーネントを含む Server のページまたはビューに移動すると、1 つ以上の JSException がスローされます。
ルーティング可能な Blazor WebAssembly コンポーネントを使用した Razor アプリのプリレンダリングおよび統合は、CSS セレクターの使用と両立しないため、これは通常の動作です。
前のセクションの例を使用している状態で、サンプル アプリで CSS セレクターが動作することを確認するだけの場合は、 App プロジェクトの Client ファイルの Program.cs ルート コンポーネントの仕様をコメント アウトしてください。
- builder.RootComponents.Add<App>("#app");
+ //builder.RootComponents.Add<App>("#app");
CSS セレクターを使用する埋め込みの Razor コンポーネント (たとえば、前の例の /razorpagescounter2) を含むページまたはビューに移動します。 ページまたはビューが埋め込みコンポーネントと共に読み込まれ、埋め込みコンポーネントは想定どおりに機能します。
プリレンダリングされた状態を保持する
プリレンダリングされた状態を保持しないと、プリレンダリング中に使用された状態は失われ、アプリが完全に読み込まれたときに再作成する必要があります。 いずれかの状態が非同期でセットアップされている場合、プリレンダリングされた UI は一時的なプレースホルダーに置き換えられてから再度完全にレンダリングされるため、UI がちらつくことがあります。
これらの問題を解決するために、Blazor では、永続コンポーネントの状態タグ ヘルパーを使用して、プリレンダリングされたページ内での状態の永続化をサポートしています。 終了 <persist-component-state /> タグの内側にタグ ヘルパーのタグ </body> を追加します。
Pages/_Layout.cshtml:
<body>
...
<persist-component-state />
</body>
PersistentComponentState サービスを使用してどの状態を永続化するか決定します。 アプリが一時停止される前に、PersistentComponentState.RegisterOnPersisting によってコールバックが登録され、コンポーネントの状態が保持されます。 状態は、アプリケーションの再開時に取得されます。 アプリのシャットダウン中に競合状態になる可能性を回避するために、初期化コードの最後に呼び出しを行います。
次の例は、FetchData プロジェクト テンプレートに基づいた、ホストされている Blazor WebAssembly アプリ内の Blazor コンポーネントの更新バージョンです。
WeatherForecastPreserveState コンポーネントは、プリレンダリング中に天気予報の状態を保持し、その後、コンポーネントを初期化するために状態を取得します。
永続コンポーネントの状態タグ ヘルパーでは、すべてのコンポーネントの呼び出しの後で、コンポーネントの状態を保持します。
Pages/WeatherForecastPreserveState.razor:
@page "/weather-forecast-preserve-state"
@implements IDisposable
@using BlazorSample.Shared
@inject IWeatherForecastService WeatherForecastService
@inject PersistentComponentState ApplicationState
<PageTitle>Weather Forecast</PageTitle>
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from the server.</p>
@if (forecasts == null)
{
<p><em>Loading...</em></p>
}
else
{
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
{
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
}
</tbody>
</table>
}
@code {
private WeatherForecast[] forecasts = Array.Empty<WeatherForecast>();
private PersistingComponentStateSubscription persistingSubscription;
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<WeatherForecast[]>(
nameof(forecasts), out var restored))
{
forecasts =
await WeatherForecastService.GetForecastAsync(DateTime.Now);
}
else
{
forecasts = restored!;
}
// Call at the end to avoid a potential race condition at app shutdown
persistingSubscription = ApplicationState.RegisterOnPersisting(PersistData);
}
private Task PersistData()
{
ApplicationState.PersistAsJson(nameof(forecasts), forecasts);
return Task.CompletedTask;
}
void IDisposable.Dispose()
{
persistingSubscription.Dispose();
}
}
プリレンダリング中に使用されたのと同じ状態でコンポーネントを初期化することにより、負荷の高い初期化ステップが 1 回だけ実行されます。 レンダリングされた UI もプリレンダリングされた UI に一致するので、ブラウザーでちらつきは発生しません。
永続化されたプリレンダリングされた状態はクライアントに転送され、そこでコンポーネントの状態を復元するために使用されます。 ホストされている Blazor WebAssembly アプリでプリレンダリングを行う場合、データはブラウザーに公開され、機密情報を含む必要はありません。
Blazor WebAssembly のその他のリソース
- アセンブリ遅延読み込みによるプリレンダリングのサポート
- プリレンダリングに関連する Razor コンポーネント ライフサイクルのトピック
-
コンポーネントの初期化 (
OnInitialized{Async}) -
コンポーネントのレンダリング後 (
OnAfterRender{Async}) - プリレンダリング後のステートフル再接続: このセクションの内容では、Blazor Server およびステートフルな SignalR の "再接続" に焦点を当てていますが、ホストされた アプリ (Blazor WebAssembly) でのプリレンダリングのシナリオでは、開発者コードを 2 回実行しないようにするための同様の条件とアプローチが必要です。WebAssemblyPrerendered プリレンダリング中に初期化コードの実行中に状態を保持するには、この記事の「プリレンダリングされた状態を保持する」セクションを参照してください。
- JavaScript 相互運用を使用したプリレンダリング
-
コンポーネントの初期化 (
- プリレンダリングに関連する認証と認可の話題
- ASP.NET Core のホストと展開Blazor WebAssembly
プリレンダリングにより、検索エンジンがページ ランクの計算に使える最初の HTTP 応答の内容がレンダリングされることで、検索エンジンの最適化 (SEO) が向上します。
ソリューションの構成
プリレンダリングの構成
ホストされる Blazor WebAssembly アプリのプリレンダリングを設定するには:
ASP.NET Core アプリで Blazor WebAssembly をホストします。 スタンドアロンの Blazor WebAssembly アプリを ASP.NET Core ソリューションに追加することも、ホスト型オプションを使用して Blazor WebAssembly プロジェクトテンプレートから作成されたホスト型の Blazor WebAssembly アプリを使用することもできます。
- Visual Studio: 追加情報 ダイアログで、ASP.NET Core Hosted チェックボックスをオンにして、Blazor WebAssemblyアプリを作成します。 この記事の例では、ソリューションに
BlazorHostedという名前が付けられています。 - Visual Studio Code/.NET CLI コマンド シェル:
dotnet new blazorwasm -ho(-ho|--hostedオプションを使用)。-o|--output {LOCATION}オプションを使用してソリューションのフォルダーを作成し、ソリューションのプロジェクト名前空間を設定します。 この記事の例では、ソリューションにBlazorHostedという名前が付けられています (dotnet new blazorwasm -ho -o BlazorHosted)。
この記事の例では、クライアント プロジェクトの名前空間は
BlazorHosted.Clientで、サーバー プロジェクトの名前空間はBlazorHosted.Serverです。- Visual Studio: 追加情報 ダイアログで、ASP.NET Core Hosted チェックボックスをオンにして、Blazor WebAssemblyアプリを作成します。 この記事の例では、ソリューションに
ファイルを削除して
wwwroot/index.htmlBlazor WebAssemblyClientプロジェクトから取り除きます。Client プロジェクトでは、 の次の行を
Program.csします。- builder.RootComponents.Add<App>("#app");Pages/_Host.cshtmlファイルを Server プロジェクトのPagesフォルダーに追加します。_Host.cshtmlファイルは、コマンド シェルで Blazor Server コマンドを使用してdotnet new blazorserver -o BlazorServerテンプレートから作成されたプロジェクトから取得できます (-o BlazorServerオプションを指定すると、プロジェクトのフォルダーが作成されます)。 ホストされるPages/_Host.cshtmlソリューションの Server プロジェクトに Blazor WebAssembly ファイルを配置した後、ファイルに次の変更を加えます。@usingプロジェクト用の Client ディレクティブを指定します (例:@using BlazorHosted.Client)。スタイルシートのリンクを、WebAssembly プロジェクトのスタイルシートをポイントするように更新します。 次の例では、クライアント プロジェクトの名前空間は
BlazorHosted.Clientになります。- <link href="css/site.css" rel="stylesheet" /> - <link href="_content/BlazorServer/_framework/scoped.styles.css" rel="stylesheet" /> + <link href="css/app.css" rel="stylesheet" /> + <link href="BlazorHosted.Client.styles.css" rel="stylesheet" />Note
ブートストラップ スタイルシート (
<link>) を要求するcss/bootstrap/bootstrap.min.css要素はそのままにしておきます。render-modeの コンポーネントタグヘルパー を更新し、Appを使用してルート WebAssemblyPrerendered コンポーネントをプリレンダリングします。- <component type="typeof(App)" render-mode="ServerPrerendered" /> + <component type="typeof(App)" render-mode="WebAssemblyPrerendered" />Blazor スクリプトのソースを、クライアント側の Blazor WebAssembly スクリプトを使用するように更新します。
- <script src="_framework/blazor.server.js"></script> + <script src="_framework/blazor.webassembly.js"></script>
Startup.Configureプロジェクトの Server で、フォールバックをindex.htmlファイルから_Host.cshtmlページに変更します。Startup.cs:- endpoints.MapFallbackToFile("index.html"); + endpoints.MapFallbackToPage("/_Host");Client プロジェクトと Server プロジェクトでプリレンダリング中に 1 つまたは複数の一般的サービスが使用される場合、両方のプロジェクトから呼び出せるメソッドにサービス登録を入れます。 詳細については、「ASP.NET Core Blazor の依存関係の挿入」を参照してください。
Server プロジェクトを実行します。 ホストされる Blazor WebAssembly アプリは、クライアントの Server プロジェクトによってプリレンダリングされ ます。
Razor コンポーネントをページまたはビューに埋め込むための構成
クライアント Razor アプリのBlazor WebAssemblyコンポーネントをサーバー アプリのページまたはビューに埋め込むためのこの記事の次のセクションと例では、追加の構成が必要です。
Razor プロジェクトの既定のServer Pages または MVC レイアウト ファイルを使用します。 Server プロジェクトには 次のファイルとフォルダーが含まれている必要があります。
Razor ページ:
Pages/Shared/_Layout.cshtmlPages/_ViewImports.cshtmlPages/_ViewStart.cshtml
MVC:
Views/Shared/_Layout.cshtmlViews/_ViewImports.cshtmlViews/_ViewStart.cshtml
Razor Pages または MVC プロジェクト テンプレートから作成されたアプリから、上記のファイルを取得します。 詳しくは、「チュートリアル: ASP.NET Core の Razor Pages の概要」または「ASP.NET Core MVC の概要」をご覧ください。
インポートされた _ViewImports.cshtml ファイル内の名前空間を、ファイルを受け取る Server プロジェクトで使用されているものと一致するように更新します。
インポートされたレイアウト ファイル (_Layout.cshtml) を、 Client プロジェクトのスタイルを含むように更新します。 次の例では、 Client プロジェクトの名前空間は BlazorHosted.Client になります。
<title> 要素は同時に更新できます。
Pages/Shared/_Layout.cshtml (Razor ページ) または Views/Shared/_Layout.cshtml (MVC):
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
- <title>@ViewData["Title"] - DonorProject</title>
+ <title>@ViewData["Title"] - BlazorHosted</title>
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" />
+ <link href="css/app.css" rel="stylesheet" />
+ <link href="BlazorHosted.Client.styles.css" rel="stylesheet" />
</head>
インポートされたレイアウトには Home および Privacy のナビゲーション リンクが含まれます。
Home リンクが、ホストされる Blazor WebAssembly アプリをポイントするようにするには、ハイパーリンクを次のように変更します。
- <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>
MVC レイアウト ファイルの場合:
- <a class="nav-link text-dark" asp-area="" asp-controller="Home"
- asp-action="Index">Home</a>
+ <a class="nav-link text-dark" href="/">Home</a>
Privacy リンクをプライバシー ページに移動するには、Server プロジェクトにプライバシー ページを追加します。
Pages/Privacy.cshtml プロジェクトの Server:
@page
@model BlazorHosted.Server.Pages.PrivacyModel
@{
}
<h1>Privacy Policy</h1>
MVC ベースのプライバシー ビューが優先される場合は、Server プロジェクトにプライバシー ビューを作成します。
View/Home/Privacy.cshtml:
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
Home コントローラーで、ビューを返します。
Controllers/HomeController.cs:
public IActionResult Privacy()
{
return View();
}
ドナー プロジェクトの Server フォルダーから wwwroot プロジェクトに静的アセットをインポートします。
-
wwwroot/cssフォルダーと内容 -
wwwroot/jsフォルダーと内容 -
wwwroot/libフォルダーと内容
ドナー プロジェクトが ASP.NET Core プロジェクト テンプレートから作成され、ファイルが変更されていない場合は、ドナー プロジェクトの wwwroot フォルダー全体を Server プロジェクトにコピー して、favicon アイコン ファイルを削除することができます。
警告
静的アセットを Client フォルダーと Serverwwwroot フォルダーの両方に配置するのは避けてください。 両方のフォルダーに同じファイルがある場合は、各フォルダー内の静的アセットが同じ Web ルート パスを共有しているため、例外がスローされます。 そのため、静的アセットを両方ではなく、いずれかの wwwroot フォルダーにホストします。
コンポーネント タグ ヘルパーを使用してページまたはビューのコンポーネントをレンダリングする
追加構成を含むソリューションを構成した後、コンポーネント タグ ヘルパーは、Blazor WebAssembly アプリのコンポーネントをページまたはビュー内にレンダリングするための 2 つのレンダー モードをサポートします。
次の Razor Pages の例では、Counter コンポーネントがページにレンダリングされます。 コンポーネントを対話形式にするために、ページのBlazor WebAssemblyに スクリプトが含まれます。
Counter コンポーネントで コンポーネントタグヘルパー ({ASSEMBLY NAME}.Pages.Counter) の完全な名前空間を使用しないようにするために、クライアントプロジェクトの @using 名前空間に Pages ディレクティブを追加します。 次の例では、 Client プロジェクトの名前空間は BlazorHosted.Client になります。
Server プロジェクトの Pages/RazorPagesCounter1.cshtml:
@page
@using BlazorHosted.Client.Pages
<component type="typeof(Counter)" render-mode="WebAssemblyPrerendered" />
@section Scripts {
<script src="_framework/blazor.webassembly.js"></script>
}
Server プロジェクトを実行します。
Razor の /razorpagescounter1 ページに移動します。 プリレンダリングされた Counter コンポーネントはページに埋め込まれています。
RenderMode によって、コンポーネントに対して以下の構成が行われます。
- ページに事前レンダリングするかどうか。
- ページに静的 HTML としてレンダリングするかどうか。または、ユーザー エージェントから Blazor アプリをブートストラップするために必要な情報が含まれているかどうか。
パラメーターの渡し方や RenderMode の構成などのコンポーネント タグ ヘルパーの詳細については、「ASP.NET Core のコンポーネント タグ ヘルパー」を参照してください。
コンポーネントによって使用される静的リソースや、アプリでのレイアウト ページの整理方法によっては、追加の作業が必要になる場合があります。 通常、ページまたはビューの Scripts レンダー セクションにスクリプトが追加され、スタイル シートはレイアウトの <head> 要素コンテンツに追加されます。
CSS セレクターを使用してページまたはビューのコンポーネントをレンダリングする
追加の構成を含めてソリューションを構成した後、Client でホストされる Blazor WebAssembly ソリューションの Program.cs プロジェクトにルート コンポーネントを追加します。 次の例では、Counter と一致する id を持つ要素を選択する CSS セレクターを使用して、ルート コンポーネントとして counter-component コンポーネントが宣言されています。 次の例では、 Client プロジェクトの名前空間は BlazorHosted.Client になります。
Program.cs プロジェクトの Client で、プロジェクトの Razor コンポーネントの名前空間をファイルの先頭に追加します。
using BlazorHosted.Client.Pages;
builder で Program.cs が確立されたら、Counter コンポーネントをルート コンポーネントとして追加します。
builder.RootComponents.Add<Counter>("#counter-component");
次の Razor Pages の例では、Counter コンポーネントがページにレンダリングされます。 コンポーネントを対話形式にするために、ページのBlazor WebAssemblyに スクリプトが含まれます。
Server プロジェクトの Pages/RazorPagesCounter2.cshtml:
@page
<div id="counter-component">Loading...</div>
@section Scripts {
<script src="_framework/blazor.webassembly.js"></script>
}
Server プロジェクトを実行します。
Razor の /razorpagescounter2 ページに移動します。 プリレンダリングされた Counter コンポーネントはページに埋め込まれています。
コンポーネントによって使用される静的リソースや、アプリでのレイアウト ページの整理方法によっては、追加の作業が必要になる場合があります。 通常、ページまたはビューの Scripts レンダー セクションにスクリプトが追加され、スタイル シートはレイアウトの <head> 要素コンテンツに追加されます。
Note
前の例では、JSException アプリが CSS セレクターを使用して Blazor WebAssembly Pages または MVC アプリにRazorプリレンダリングおよび統合された場合に がスローされます。 Client プロジェクトの Razor コンポーネントのいずれかに移動すると、次の例外がスローされます。
Microsoft.JSInterop.JSException: Could not find any element matching selector '#counter-component'.(Microsoft.JSInterop.JSException: セレクター '#counter-component' に一致する要素が見つかりませんでした。)
ルーティング可能な Blazor WebAssembly コンポーネントを使用した Razor アプリのプリレンダリングおよび統合は、CSS セレクターの使用と両立しないため、これは通常の動作です。
Blazor WebAssembly のその他のリソース
- アセンブリ遅延読み込みによるプリレンダリングのサポート
- プリレンダリングに関連する Razor コンポーネント ライフサイクルのトピック
-
コンポーネントの初期化 (
OnInitialized{Async}) -
コンポーネントのレンダリング後 (
OnAfterRender{Async}) - プリレンダリング後のステートフル再接続: このセクションの内容では、Blazor Server およびステートフルな SignalR の "再接続" に焦点を当てていますが、ホストされた アプリ (Blazor WebAssembly) でのプリレンダリングのシナリオでは、開発者コードを 2 回実行しないようにするための同様の条件とアプローチが必要です。WebAssemblyPrerendered プリレンダリング中に初期化コードの実行中に状態を保持するには、この記事の「プリレンダリングされた状態を保持する」セクションを参照してください。
- JavaScript 相互運用を使用したプリレンダリング
-
コンポーネントの初期化 (
- プリレンダリングに関連する認証と認可の話題
- ASP.NET Core のホストと展開Blazor WebAssembly
Razor コンポーネントをホストRazorソリューションの Blazor WebAssembly Pages または MVC アプリに統合するは、.NET 5 以降の ASP.NET Core でサポートされています。 この記事の .NET 5 以降のバージョンを選択してください。
ASP.NET Core