ASP.NET Core でのアセンブリの遅延読み込みBlazor WebAssembly
Blazor WebAssembly アプリケーションの起動パフォーマンスは、アセンブリが必要になるまでアプリケーション アセンブリの読み込みを待機することで改善できます。これは "遅延読み込み" と呼ばれます。
この記事の初めのセクションでは、アプリの構成について説明します。 実際の動作のデモンストレーションについては、この記事の最後にある「コード例全体」のセクションを参照してください。
この記事は Blazor WebAssembly アプリにのみ該当します。 Blazor Server アプリでは、アセンブリの遅延読み込みを行っても効果がありません。これは、Blazor Server アプリのアセンブリがクライアントにダウンロードされないためです。
プロジェクト ファイルの構成
アプリのプロジェクト ファイル (.csproj
) 内で、BlazorWebAssemblyLazyLoad
項目を使用して、遅延読み込みのマークをアセンブリに付けます。 .dll
拡張子が含まれるアセンブリ名を使用します。 Blazor フレームワークを使用すると、アプリの起動時にアセンブリは読み込まれません。
<ItemGroup>
<BlazorWebAssemblyLazyLoad Include="{ASSEMBLY NAME}.dll" />
</ItemGroup>
{ASSEMBLY NAME}
プレースホルダーは、アセンブリの名前です。 .dll
ファイル拡張子が必要です。
アセンブリごとに 1 個の BlazorWebAssemblyLazyLoad
項目を含めます。 アセンブリに依存関係がある場合は、依存関係ごとに BlazorWebAssemblyLazyLoad
エントリを含めます。
Router
コンポーネントの構成
Blazor フレームワークを使用すると、クライアント側の Blazor WebAssembly アプリ† である LazyAssemblyLoader で、アセンブリの遅延読み込みを行うシングルトン サービスが自動的に登録されます。 LazyAssemblyLoader.LoadAssembliesAsync メソッド:
- JS 相互運用を使用して、ネットワーク呼び出しを介してアセンブリをフェッチします。
- ブラウザー内の WebAssembly で実行されているランタイムにアセンブリを読み込みます。
†"ホストされた" Blazor WebAssemblyソリューションに関するガイダンスについては、「ホストされた Blazor WebAssembly ソリューションでのアセンブリの遅延読み込み」のセクションを参照してください。
Blazor の Router は、Blazor がルーティング可能なコンポーネントを求めて探索するアセンブリを指定し、ユーザーが移動するルートのコンポーネントをレンダリングする役割も担うコンポーネントです。 Router コンポーネントの OnNavigateAsync
メソッドは、ユーザーが要求するエンドポイント用の正しいアセンブリを読み込むために、遅延読み込みと組み合わせて使用されます。
LazyAssemblyLoader を使用して読み込むアセンブリを決定するロジックは、OnNavigateAsync 内に実装されています。 ロジックを構成する方法のオプションは次のとおりです。
- OnNavigateAsync メソッド内での条件チェック。
- ルートをアセンブリ名にマップするルックアップ テーブル (コンポーネントに挿入されるか、
@code
ブロック内に実装される)。
次に例を示します。
- Microsoft.AspNetCore.Components.WebAssembly.Services の名前空間が指定されています。
- LazyAssemblyLoader サービスが挿入されます (
AssemblyLoader
)。 {PATH}
プレースホルダーは、アセンブリのリストを読み込む場所のパスです。 この例では、1 つのアセンブリ セットを読み込む 1 つのパスの条件付きチェックを使用しています。{LIST OF ASSEMBLIES}
プレースホルダーは、.dll
拡張子 (たとえば、"Assembly1.dll", "Assembly2.dll"
) を含めたアセンブリのファイル名文字列のコンマ区切りのリストです。
App.razor
:
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger
<Router AppAssembly="@typeof(App).Assembly"
OnNavigateAsync="@OnNavigateAsync">
...
</Router>
@code {
private async Task OnNavigateAsync(NavigationContext args)
{
try
{
if (args.Path == "{PATH}")
{
var assemblies = await AssemblyLoader.LoadAssembliesAsync(
new[] { {LIST OF ASSEMBLIES} });
}
}
catch (Exception ex)
{
Logger.LogError("Error: {Message}", ex.Message);
}
}
}
注意
前の例には、Router
コンポーネントの Razor マークアップ (...
) の内容が示されていません。 完全なコードのデモンストレーションについては、この記事の「コード例全体」のセクションを参照してください。
ルーティング可能なコンポーネントを含むアセンブリ
アセンブリの一覧にルーティング可能なコンポーネントが含まれている場合は、指定されたパスのアセンブリ リストが Router
コンポーネントの AdditionalAssemblies コレクションに渡されます。
次に例を示します。
lazyLoadedAssemblies
の List<Assembly> で AdditionalAssemblies にアセンブリ リストが渡されます。 フレームワークにおいてルートがアセンブリ内で検索され、新しいルートが見つかった場合はルート コレクションが更新されます。 Assembly 型にアクセスするために、System.Reflection の名前空間がApp.razor
ファイルの先頭に含まれています。{PATH}
プレースホルダーは、アセンブリのリストを読み込む場所のパスです。 この例では、1 つのアセンブリ セットを読み込む 1 つのパスの条件付きチェックを使用しています。{LIST OF ASSEMBLIES}
プレースホルダーは、.dll
拡張子 (たとえば、"Assembly1.dll", "Assembly2.dll"
) を含めたアセンブリのファイル名文字列のコンマ区切りのリストです。
App.razor
:
@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger
<Router AppAssembly="@typeof(App).Assembly"
AdditionalAssemblies="@lazyLoadedAssemblies"
OnNavigateAsync="@OnNavigateAsync">
...
</Router>
@code {
private List<Assembly> lazyLoadedAssemblies = new();
private async Task OnNavigateAsync(NavigationContext args)
{
try
{
if (args.Path == "{PATH}")
{
var assemblies = await AssemblyLoader.LoadAssembliesAsync(
new[] { {LIST OF ASSEMBLIES} });
lazyLoadedAssemblies.AddRange(assemblies);
}
}
catch (Exception ex)
{
Logger.LogError("Error: {Message}", ex.Message);
}
}
}
注意
前の例には、Router
コンポーネントの Razor マークアップ (...
) の内容が示されていません。 完全なコードのデモンストレーションについては、この記事の「コード例全体」のセクションを参照してください。
詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。
<Navigating>
コンテンツとのユーザー操作
アセンブリの読み込み中 (数秒かかることがある)、Router コンポーネントで Navigating プロパティを使用して、ページの切り替えが行われていることをユーザーに示すことができます。
詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。
OnNavigateAsync
でキャンセルを処理する
OnNavigateAsync コールバックに渡される NavigationContext オブジェクトには、新しいナビゲーション イベントが発生したときに設定される CancellationToken が含まれています。 そのキャンセル トークンが、古いナビゲーションに対して OnNavigateAsync コールバックを継続して実行しないように設定されている場合は、OnNavigateAsync コールバックをスローする必要があります。
詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。
OnNavigateAsync
イベントと名前が変更されたアセンブリ ファイル
リソース ローダーでは、blazor.boot.json
ファイルで定義されているアセンブリ名が使用されます。 アセンブリの名前が変更された場合、OnNavigateAsync コールバックで使用されるアセンブリ名と blazor.boot.json
ファイル内のアセンブリ名が同期されなくなります。
これを修正するには:
- 使用するアセンブリ名を決定するときに、アプリが
Production
環境で実行されているかどうかを確認します。 - 名前を変更したアセンブリ名を別のファイルに格納し、そのファイルから読み取りを行い、LazyAssemblyLoader サービスおよび OnNavigateAsync コールバックで使用するアセンブリ名を決定します。
ホストされた Blazor WebAssembly ソリューションでのアセンブリの遅延読み込み
フレームワークの遅延読み込みの実装では、ホストされた Blazor WebAssemblyソリューションでのプリレンダリングによる遅延読み込みがサポートされます。 プリレンダリング中は、遅延読み込みのマークが付けられたものも含め、すべてのアセンブリが読み込まれると見なされます。 Server プロジェクトに LazyAssemblyLoader サービスを手動で登録します。
Server プロジェクトの Program.cs
ファイルの先頭に、Microsoft.AspNetCore.Components.WebAssembly.Services の名前空間を追加します。
using Microsoft.AspNetCore.Components.WebAssembly.Services;
Server プロジェクトの Program.cs
で、サービスを登録します。
builder.Services.AddScoped<LazyAssemblyLoader>();
コード例全体
このセクションのデモンストレーションの内容は次のとおりです。
Robot
コンポーネント (/robot
のルート テンプレートを使用したRobot.razor
) を含む Razor クラス ライブラリ (RCL) としてロボット コントロール アセンブリ (GrantImaharaRobotControls.dll
) を作成します。- ユーザーから
Robot
の URL が要求されたときに/robot
コンポーネントをレンダリングするために、RCL のアセンブリを遅延読み込みします。
新しい ASP.NET Core クラス ライブラリ プロジェクトを作成します。
- Visual Studio: [ソリューションの作成]>[新しいプロジェクトの作成]>Razor [クラス ライブラリ] 。 プロジェクトに
GrantImaharaRobotControls
という名前を付けます。 - Visual Studio Code/.NET CLI: コマンド プロンプトから
dotnet new razorclasslib -o GrantImaharaRobotControls
を実行します。-o|--output
オプションでソリューションのフォルダーを作成し、プロジェクトにGrantImaharaRobotControls
という名前を付けます。
- Visual Studio: [ソリューションの作成]>[新しいプロジェクトの作成]>Razor [クラス ライブラリ] 。 プロジェクトに
このセクションの後半に示すコンポーネント例では、Blazor フォームを使用します。 RCL プロジェクトに、
Microsoft.AspNetCore.Components.Forms
パッケージを追加します。Note
.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。
ロボットに親指を立てるジェスチャを実行させると仮定した
ThumbUp
メソッドを使用して、RCL にHandGesture
クラスを作成します。 そのメソッドに軸の引数 (Left
またはRight
) をenum
として渡します。 メソッドが正常に終了すると、true
が返されます。HandGesture.cs
:using Microsoft.Extensions.Logging; namespace GrantImaharaRobotControls { public static class HandGesture { public static bool ThumbUp(Axis axis, ILogger logger) { logger.LogInformation("Thumb up gesture. Axis: {Axis}", axis); // Code to make robot perform gesture return true; } } public enum Axis { Left, Right } }
次のコンポーネントを RCL プロジェクトのルートに追加します。 このコンポーネントでユーザーは左手または右手の親指を立てるジェスチャ要求を送信できます。
Robot.razor
:@page "/robot" @using Microsoft.AspNetCore.Components.Forms @using Microsoft.Extensions.Logging @inject ILogger<Robot> Logger <h1>Robot</h1> <EditForm Model="@robotModel" OnValidSubmit="@HandleValidSubmit"> <InputRadioGroup @bind-Value="robotModel.AxisSelection"> @foreach (var entry in (Axis[])Enum .GetValues(typeof(Axis))) { <InputRadio Value="@entry" /> <text> </text>@entry<br> } </InputRadioGroup> <button type="submit">Submit</button> </EditForm> <p> @message </p> @code { private RobotModel robotModel = new() { AxisSelection = Axis.Left }; private string? message; private void HandleValidSubmit() { Logger.LogInformation("HandleValidSubmit called"); var result = HandGesture.ThumbUp(robotModel.AxisSelection, Logger); message = $"ThumbUp returned {result} at {DateTime.Now}."; } public class RobotModel { public Axis AxisSelection { get; set; } } }
RCL のアセンブリの遅延読み込みの動作を示す Blazor WebAssembly アプリを作成します。
Visual Studio、Visual Studio Code、または .NET CLI のコマンド プロンプトを使用して、Blazor WebAssembly アプリを作成します。 プロジェクトに
LazyLoadTest
という名前を付けます。GrantImaharaRobotControls
RCL に対するプロジェクト参照を作成します。- Visual Studio:
GrantImaharaRobotControls
RCL プロジェクトをソリューションに追加します ( [追加]>[既存のプロジェクト] )。 [追加]>[プロジェクト参照] を選択して、GrantImaharaRobotControls
RCL に対するプロジェクト参照を追加します。 - Visual Studio Code/.NET CLI: プロジェクトのフォルダーからコマンド シェルで
dotnet add reference {PATH}
を実行します。{PATH}
プレースホルダーは、RCL プロジェクトへのパスです。
- Visual Studio:
アプリケーションをビルドし、実行します。 Index
コンポーネントを読み込む既定のページ (Pages/Index.razor
) について、開発者ツールの [ネットワーク] タブに RCL のアセンブリ GrantImaharaRobotControls.dll
が読み込まれていることが示されます。 Index
コンポーネントにそのアセンブリは使用されないため、アセンブリを読み込むことは効率的ではありません。
GrantImaharaRobotControls.dll
アセンブリを遅延読み込みするようにアプリを構成します。
Blazor WebAssembly アプリのプロジェクト ファイル (
.csproj
) で、遅延読み込みの対象である RCL のアセンブリを指定します。<ItemGroup> <BlazorWebAssemblyLazyLoad Include="GrantImaharaRobotControls.dll" /> </ItemGroup>
次の Router コンポーネントによって、ユーザーが
/robot
に移動したときにGrantImaharaRobotControls.dll
アセンブリを読み込む方法が実演されています。 アプリの既定のApp
コンポーネントを次のApp
コンポーネントに置き換えます。ページの切り替え中に、スタイル付きのメッセージが
<Navigating>
要素を使用してユーザーに表示されます。 詳細については、「<Navigating>
コンテンツとのユーザー操作」のセクションを参照してください。アセンブリは AdditionalAssemblies に割り当てられます。その結果、ルーターによってアセンブリでルーティング可能なコンポーネントが検索され、そこで
Robot
コンポーネントが見つかります。Robot
コンポーネントのルートがアプリのルート コレクションに追加されます。 詳しくは、記事「ASP.NET Core の Blazor ルーティングとナビゲーション」と、この記事の「ルーティング可能なコンポーネントを含むアセンブリ」セクションをご覧ください。App.razor
:@using System.Reflection @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.WebAssembly.Services @using Microsoft.Extensions.Logging @inject LazyAssemblyLoader AssemblyLoader @inject ILogger<App> Logger <Router AppAssembly="@typeof(App).Assembly" AdditionalAssemblies="@lazyLoadedAssemblies" OnNavigateAsync="@OnNavigateAsync"> <Navigating> <div style="padding:20px;background-color:blue;color:white"> <p>Loading the requested page…</p> </div> </Navigating> <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> @code { private List<Assembly> lazyLoadedAssemblies = new(); private async Task OnNavigateAsync(NavigationContext args) { try { if (args.Path == "robot") { var assemblies = await AssemblyLoader.LoadAssembliesAsync( new[] { "GrantImaharaRobotControls.dll" }); lazyLoadedAssemblies.AddRange(assemblies); } } catch (Exception ex) { Logger.LogError("Error: {Message}", ex.Message); } } }
もう一度アプリをビルドし、実行します。 Index
コンポーネントを読み込む既定のページ (Pages/Index.razor
) について、開発者ツールの [ネットワーク] タブに、RCL のアセンブリ (GrantImaharaRobotControls.dll
) が Index
コンポーネント用に読み込まれて "いない" ことが示されます。
RCL の Robot
コンポーネントが /robot
で要求されると、GrantImaharaRobotControls.dll
アセンブリが読み込まれ、Robot
コンポーネントがレンダリングされます。
トラブルシューティング
- レンダリングが予期しないものになった (たとえば、前のナビゲーションのコンポーネントがレンダリングされた) 場合は、キャンセル トークンが設定されている場合にコードがスローされることをご確認ください。
- 遅延読み込み用に構成したアセンブリがアプリの開始時に予期せず読み込まれた場合は、プロジェクト ファイルでアセンブリが遅延読み込み対象としてマークされていることを確認します。
その他のリソース
Blazor WebAssembly アプリケーションの起動パフォーマンスは、アセンブリが必要になるまでアプリケーション アセンブリの読み込みを待機することで改善できます。これは "遅延読み込み" と呼ばれます。
この記事の初めのセクションでは、アプリの構成について説明します。 実際の動作のデモンストレーションについては、この記事の最後にある「コード例全体」のセクションを参照してください。
Note
Blazor Server アプリでは、アセンブリの遅延読み込みを行っても効果がありません。これは、Blazor Server アプリのアセンブリがクライアントにダウンロードされないためです。
プロジェクト ファイルの構成
アプリのプロジェクト ファイル (.csproj
) 内で、BlazorWebAssemblyLazyLoad
項目を使用して、遅延読み込みのマークをアセンブリに付けます。 .dll
拡張子が含まれるアセンブリ名を使用します。 Blazor フレームワークを使用すると、アプリの起動時にアセンブリは読み込まれません。
<ItemGroup>
<BlazorWebAssemblyLazyLoad Include="{ASSEMBLY NAME}.dll" />
</ItemGroup>
{ASSEMBLY NAME}
プレースホルダーは、アセンブリの名前です。 .dll
ファイル拡張子が必要です。
アセンブリごとに 1 個の BlazorWebAssemblyLazyLoad
項目を含めます。 アセンブリに依存関係がある場合は、依存関係ごとに BlazorWebAssemblyLazyLoad
エントリを含めます。
Router
コンポーネントの構成
Blazor フレームワークを使用すると、クライアント側の Blazor WebAssembly アプリ† である LazyAssemblyLoader で、アセンブリの遅延読み込みを行うシングルトン サービスが自動的に登録されます。 LazyAssemblyLoader.LoadAssembliesAsync メソッド:
- JS 相互運用を使用して、ネットワーク呼び出しを介してアセンブリをフェッチします。
- ブラウザー内の WebAssembly で実行されているランタイムにアセンブリを読み込みます。
†"ホストされた" Blazor WebAssemblyソリューションに関するガイダンスについては、「ホストされた Blazor WebAssembly ソリューションでのアセンブリの遅延読み込み」のセクションを参照してください。
Blazor の Router は、Blazor がルーティング可能なコンポーネントを求めて探索するアセンブリを指定し、ユーザーが移動するルートのコンポーネントをレンダリングする役割も担うコンポーネントです。 Router コンポーネントの OnNavigateAsync
メソッドは、ユーザーが要求するエンドポイント用の正しいアセンブリを読み込むために、遅延読み込みと組み合わせて使用されます。
LazyAssemblyLoader を使用して読み込むアセンブリを決定するロジックは、OnNavigateAsync 内に実装されています。 ロジックを構成する方法のオプションは次のとおりです。
- OnNavigateAsync メソッド内での条件チェック。
- ルートをアセンブリ名にマップするルックアップ テーブル (コンポーネントに挿入されるか、
@code
ブロック内に実装される)。
次に例を示します。
- Microsoft.AspNetCore.Components.WebAssembly.Services の名前空間が指定されています。
- LazyAssemblyLoader サービスが挿入されます (
AssemblyLoader
)。 {PATH}
プレースホルダーは、アセンブリのリストを読み込む場所のパスです。 この例では、1 つのアセンブリ セットを読み込む 1 つのパスの条件付きチェックを使用しています。{LIST OF ASSEMBLIES}
プレースホルダーは、.dll
拡張子 (たとえば、"Assembly1.dll", "Assembly2.dll"
) を含めたアセンブリのファイル名文字列のコンマ区切りのリストです。
App.razor
:
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger
<Router AppAssembly="@typeof(Program).Assembly"
OnNavigateAsync="@OnNavigateAsync">
...
</Router>
@code {
private async Task OnNavigateAsync(NavigationContext args)
{
try
{
if (args.Path == "{PATH}")
{
var assemblies = await AssemblyLoader.LoadAssembliesAsync(
new[] { {LIST OF ASSEMBLIES} });
}
}
catch (Exception ex)
{
Logger.LogError("Error: {Message}", ex.Message);
}
}
}
注意
前の例には、Router
コンポーネントの Razor マークアップ (...
) の内容が示されていません。 完全なコードのデモンストレーションについては、この記事の「コード例全体」のセクションを参照してください。
Note
ASP.NET Core 5.0.1 のリリースと、その他の 5.x リリースでは、Router
コンポーネントに @true
に設定された PreferExactMatches
パラメーターが含まれています。 詳細については、「ASP.NET Core 3.1 から 5.0 への移行」を参照してください。
ルーティング可能なコンポーネントを含むアセンブリ
アセンブリの一覧にルーティング可能なコンポーネントが含まれている場合は、指定されたパスのアセンブリ リストが Router
コンポーネントの AdditionalAssemblies コレクションに渡されます。
次に例を示します。
lazyLoadedAssemblies
の List<Assembly> で AdditionalAssemblies にアセンブリ リストが渡されます。 フレームワークにおいてルートがアセンブリ内で検索され、新しいルートが見つかった場合はルート コレクションが更新されます。 Assembly 型にアクセスするために、System.Reflection の名前空間がApp.razor
ファイルの先頭に含まれています。{PATH}
プレースホルダーは、アセンブリのリストを読み込む場所のパスです。 この例では、1 つのアセンブリ セットを読み込む 1 つのパスの条件付きチェックを使用しています。{LIST OF ASSEMBLIES}
プレースホルダーは、.dll
拡張子 (たとえば、"Assembly1.dll", "Assembly2.dll"
) を含めたアセンブリのファイル名文字列のコンマ区切りのリストです。
App.razor
:
@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger
<Router AppAssembly="@typeof(Program).Assembly"
AdditionalAssemblies="@lazyLoadedAssemblies"
OnNavigateAsync="@OnNavigateAsync">
...
</Router>
@code {
private List<Assembly> lazyLoadedAssemblies = new();
private async Task OnNavigateAsync(NavigationContext args)
{
try
{
if (args.Path == "{PATH}")
{
var assemblies = await AssemblyLoader.LoadAssembliesAsync(
new[] { {LIST OF ASSEMBLIES} });
lazyLoadedAssemblies.AddRange(assemblies);
}
}
catch (Exception ex)
{
Logger.LogError("Error: {Message}", ex.Message);
}
}
}
注意
前の例には、Router
コンポーネントの Razor マークアップ (...
) の内容が示されていません。 完全なコードのデモンストレーションについては、この記事の「コード例全体」のセクションを参照してください。
Note
ASP.NET Core 5.0.1 のリリースと、その他の 5.x リリースでは、Router
コンポーネントに @true
に設定された PreferExactMatches
パラメーターが含まれています。 詳細については、「ASP.NET Core 3.1 から 5.0 への移行」を参照してください。
詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。
<Navigating>
コンテンツとのユーザー操作
アセンブリの読み込み中 (数秒かかることがある)、Router コンポーネントで Navigating プロパティを使用して、ページの切り替えが行われていることをユーザーに示すことができます。
詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。
OnNavigateAsync
でキャンセルを処理する
OnNavigateAsync コールバックに渡される NavigationContext オブジェクトには、新しいナビゲーション イベントが発生したときに設定される CancellationToken が含まれています。 そのキャンセル トークンが、古いナビゲーションに対して OnNavigateAsync コールバックを継続して実行しないように設定されている場合は、OnNavigateAsync コールバックをスローする必要があります。
詳細については、「ASP.NET Core の Blazor ルーティングとナビゲーション」を参照してください。
OnNavigateAsync
イベントと名前が変更されたアセンブリ ファイル
リソース ローダーでは、blazor.boot.json
ファイルで定義されているアセンブリ名が使用されます。 アセンブリの名前が変更された場合、OnNavigateAsync コールバックで使用されるアセンブリ名と blazor.boot.json
ファイル内のアセンブリ名が同期されなくなります。
これを修正するには:
- 使用するアセンブリ名を決定するときに、アプリが
Production
環境で実行されているかどうかを確認します。 - 名前を変更したアセンブリ名を別のファイルに格納し、そのファイルから読み取りを行い、LazyAssemblyLoader サービスおよび OnNavigateAsync コールバックで使用するアセンブリ名を決定します。
ホストされた Blazor WebAssembly ソリューションでのアセンブリの遅延読み込み
フレームワークの遅延読み込みの実装では、ホストされた Blazor WebAssemblyソリューションでのプリレンダリングによる遅延読み込みがサポートされます。 プリレンダリング中は、遅延読み込みのマークが付けられたものも含め、すべてのアセンブリが読み込まれると見なされます。 Server プロジェクトに LazyAssemblyLoader サービスを手動で登録します。
Server プロジェクトの Startup.cs
ファイルの先頭に、Microsoft.AspNetCore.Components.WebAssembly.Services の名前空間を追加します。
using Microsoft.AspNetCore.Components.WebAssembly.Services;
Server プロジェクトの Startup.ConfigureServices
メソッド (Startup.cs
) で、サービスを登録します。
services.AddScoped<LazyAssemblyLoader>();
コード例全体
このセクションのデモンストレーションの内容は次のとおりです。
Robot
コンポーネント (/robot
のルート テンプレートを使用したRobot.razor
) を含む Razor クラス ライブラリ (RCL) としてロボット コントロール アセンブリ (GrantImaharaRobotControls.dll
) を作成します。- ユーザーから
Robot
の URL が要求されたときに/robot
コンポーネントをレンダリングするために、RCL のアセンブリを遅延読み込みします。
新しい ASP.NET Core クラス ライブラリ プロジェクトを作成します。
- Visual Studio: [ソリューションの作成]>[新しいプロジェクトの作成]>Razor [クラス ライブラリ] 。 プロジェクトに
GrantImaharaRobotControls
という名前を付けます。 - Visual Studio Code/.NET CLI: コマンド プロンプトから
dotnet new razorclasslib -o GrantImaharaRobotControls
を実行します。-o|--output
オプションでソリューションのフォルダーを作成し、プロジェクトにGrantImaharaRobotControls
という名前を付けます。
- Visual Studio: [ソリューションの作成]>[新しいプロジェクトの作成]>Razor [クラス ライブラリ] 。 プロジェクトに
このセクションの後半に示すコンポーネント例では、Blazor フォームを使用します。 RCL プロジェクトに、
Microsoft.AspNetCore.Components.Forms
パッケージを追加します。Note
.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。
ロボットに親指を立てるジェスチャを実行させると仮定した
ThumbUp
メソッドを使用して、RCL にHandGesture
クラスを作成します。 そのメソッドに軸の引数 (Left
またはRight
) をenum
として渡します。 メソッドが正常に終了すると、true
が返されます。HandGesture.cs
:using Microsoft.Extensions.Logging; namespace GrantImaharaRobotControls { public static class HandGesture { public static bool ThumbUp(Axis axis, ILogger logger) { logger.LogInformation("Thumb up gesture. Axis: {Axis}", axis); // Code to make robot perform gesture return true; } } public enum Axis { Left, Right } }
次のコンポーネントを RCL プロジェクトのルートに追加します。 このコンポーネントでユーザーは左手または右手の親指を立てるジェスチャ要求を送信できます。
Robot.razor
:@page "/robot" @using Microsoft.AspNetCore.Components.Forms @using Microsoft.Extensions.Logging @inject ILogger<Robot> Logger <h1>Robot</h1> <EditForm Model="@robotModel" OnValidSubmit="@HandleValidSubmit"> <InputRadioGroup @bind-Value="robotModel.AxisSelection"> @foreach (var entry in (Axis[])Enum .GetValues(typeof(Axis))) { <InputRadio Value="@entry" /> <text> </text>@entry<br> } </InputRadioGroup> <button type="submit">Submit</button> </EditForm> <p> @message </p> @code { private RobotModel robotModel = new() { AxisSelection = Axis.Left }; private string message; private void HandleValidSubmit() { Logger.LogInformation("HandleValidSubmit called"); var result = HandGesture.ThumbUp(robotModel.AxisSelection, Logger); message = $"ThumbUp returned {result} at {DateTime.Now}."; } public class RobotModel { public Axis AxisSelection { get; set; } } }
RCL のアセンブリの遅延読み込みの動作を示す Blazor WebAssembly アプリを作成します。
Visual Studio、Visual Studio Code、または .NET CLI のコマンド プロンプトを使用して、Blazor WebAssembly アプリを作成します。 プロジェクトに
LazyLoadTest
という名前を付けます。GrantImaharaRobotControls
RCL に対するプロジェクト参照を作成します。- Visual Studio:
GrantImaharaRobotControls
RCL プロジェクトをソリューションに追加します ([追加]>[既存のプロジェクト])。 [追加]>[プロジェクト参照] を選択して、GrantImaharaRobotControls
RCL に対するプロジェクト参照を追加します。 - Visual Studio Code/.NET CLI: プロジェクトのフォルダーからコマンド シェルで
dotnet add reference {PATH}
を実行します。{PATH}
プレースホルダーは、RCL プロジェクトへのパスです。
- Visual Studio:
アプリケーションをビルドし、実行します。 Index
コンポーネントを読み込む既定のページ (Pages/Index.razor
) について、開発者ツールの [ネットワーク] タブに RCL のアセンブリ GrantImaharaRobotControls.dll
が読み込まれていることが示されます。 Index
コンポーネントにそのアセンブリは使用されないため、アセンブリを読み込むことは効率的ではありません。
GrantImaharaRobotControls.dll
アセンブリを遅延読み込みするようにアプリを構成します。
Blazor WebAssembly アプリのプロジェクト ファイル (
.csproj
) で、遅延読み込みの対象である RCL のアセンブリを指定します。<ItemGroup> <BlazorWebAssemblyLazyLoad Include="GrantImaharaRobotControls.dll" /> </ItemGroup>
次の Router コンポーネントによって、ユーザーが
/robot
に移動したときにGrantImaharaRobotControls.dll
アセンブリを読み込む方法が実演されています。 アプリの既定のApp
コンポーネントを次のApp
コンポーネントに置き換えます。ページの切り替え中に、スタイル付きのメッセージが
<Navigating>
要素を使用してユーザーに表示されます。 詳細については、「<Navigating>
コンテンツとのユーザー操作」のセクションを参照してください。アセンブリは AdditionalAssemblies に割り当てられます。その結果、ルーターによってアセンブリでルーティング可能なコンポーネントが検索され、そこで
Robot
コンポーネントが見つかります。Robot
コンポーネントのルートがアプリのルート コレクションに追加されます。 詳しくは、記事「ASP.NET Core の Blazor ルーティングとナビゲーション」と、この記事の「ルーティング可能なコンポーネントを含むアセンブリ」セクションをご覧ください。App.razor
:@using System.Reflection @using Microsoft.AspNetCore.Components.Routing @using Microsoft.AspNetCore.Components.WebAssembly.Services @using Microsoft.Extensions.Logging @inject LazyAssemblyLoader AssemblyLoader @inject ILogger<App> Logger <Router AppAssembly="@typeof(Program).Assembly" AdditionalAssemblies="@lazyLoadedAssemblies" OnNavigateAsync="@OnNavigateAsync"> <Navigating> <div style="padding:20px;background-color:blue;color:white"> <p>Loading the requested page…</p> </div> </Navigating> <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> @code { private List<Assembly> lazyLoadedAssemblies = new(); private async Task OnNavigateAsync(NavigationContext args) { try { if (args.Path == "robot") { var assemblies = await AssemblyLoader.LoadAssembliesAsync( new[] { "GrantImaharaRobotControls.dll" }); lazyLoadedAssemblies.AddRange(assemblies); } } catch (Exception ex) { Logger.LogError("Error: {Message}", ex.Message); } } }
注意
ASP.NET Core 5.0.1 のリリースと、その他の 5.x リリースでは、
Router
コンポーネントに@true
に設定されたPreferExactMatches
パラメーターが含まれています。 詳細については、「ASP.NET Core 3.1 から 5.0 への移行」を参照してください。
もう一度アプリをビルドし、実行します。 Index
コンポーネントを読み込む既定のページ (Pages/Index.razor
) について、開発者ツールの [ネットワーク] タブに、RCL のアセンブリ (GrantImaharaRobotControls.dll
) が Index
コンポーネント用に読み込まれて "いない" ことが示されます。
RCL の Robot
コンポーネントが /robot
で要求されると、GrantImaharaRobotControls.dll
アセンブリが読み込まれ、Robot
コンポーネントがレンダリングされます。
トラブルシューティング
- レンダリングが予期しないものになった (たとえば、前のナビゲーションのコンポーネントがレンダリングされた) 場合は、キャンセル トークンが設定されている場合にコードがスローされることをご確認ください。
- 遅延読み込み用に構成したアセンブリがアプリの開始時に予期せず読み込まれた場合は、プロジェクト ファイルでアセンブリが遅延読み込み対象としてマークされていることを確認します。
Note
遅延読み込みされたアセンブリから型を読み込む場合、既知の問題が存在します。 詳細については、「Blazor WebAssembly lazy loading assemblies not working when using @ref attribute in the component (dotnet/aspnetcore #29342)」を参照してください。