AspNetCore.Docs/aspnetcore/blazor/webassembly-lazy-load-assem...

693 lines
26 KiB
Markdown
Raw Permalink Normal View History

2021-08-09 03:43:46 +08:00
---
title: Lazy load assemblies in ASP.NET Core Blazor WebAssembly
author: guardrex
2022-03-15 17:53:00 +08:00
description: Discover how to lazy load assemblies in Blazor WebAssembly apps.
2021-08-09 03:43:46 +08:00
monikerRange: '>= aspnetcore-5.0'
ms.author: riande
ms.custom: mvc
2024-11-18 21:14:57 +08:00
ms.date: 11/12/2024
2021-08-09 03:43:46 +08:00
uid: blazor/webassembly-lazy-load-assemblies
---
# Lazy load assemblies in ASP.NET Core Blazor WebAssembly
[!INCLUDE[](~/includes/not-latest-version.md)]
2023-04-04 23:06:06 +08:00
Blazor WebAssembly app startup performance can be improved by waiting to load developer-created app assemblies until the assemblies are required, which is called *lazy loading*.
This article's initial sections cover the app configuration. For a working demonstration, see the [Complete example](#complete-example) section at the end of this article.
*This article only applies to Blazor WebAssembly apps.* Assembly lazy loading doesn't benefit server-side apps because server-rendered apps don't download assemblies to the client.
Lazy loading shouldn't be used for core runtime assemblies, which might be trimmed on publish and unavailable on the client when the app loads.
## File extension placeholder (`{FILE EXTENSION}`) for assembly files
:::moniker range=">= aspnetcore-8.0"
Assembly files use the [Webcil packaging format for .NET assemblies](xref:blazor/host-and-deploy/webassembly#webcil-packaging-format-for-net-assemblies) with a `.wasm` file extension.
Throughout the article, the `{FILE EXTENSION}` placeholder represents "`wasm`".
:::moniker-end
:::moniker range="< aspnetcore-8.0"
2021-08-09 03:43:46 +08:00
Assembly files are based on Dynamic-Link Libraries (DLLs) with a `.dll` file extension.
2021-08-09 03:43:46 +08:00
Throughout the article, the `{FILE EXTENSION}` placeholder represents "`dll`".
:::moniker-end
2021-08-09 03:43:46 +08:00
## Project file configuration
Mark assemblies for lazy loading in the app's project file (`.csproj`) using the `BlazorWebAssemblyLazyLoad` item. Use the assembly name with file extension. The Blazor framework prevents the assembly from loading at app launch.
2021-08-09 03:43:46 +08:00
```xml
<ItemGroup>
<BlazorWebAssemblyLazyLoad Include="{ASSEMBLY NAME}.{FILE EXTENSION}" />
2021-08-09 03:43:46 +08:00
</ItemGroup>
```
The `{ASSEMBLY NAME}` placeholder is the name of the assembly, and the `{FILE EXTENSION}` placeholder is the file extension. The file extension is required.
2021-08-09 03:43:46 +08:00
Include one `BlazorWebAssemblyLazyLoad` item for each assembly. If an assembly has dependencies, include a `BlazorWebAssemblyLazyLoad` entry for each dependency.
## `Router` component configuration
2023-09-07 23:53:28 +08:00
The Blazor framework automatically registers a singleton service for lazy loading assemblies in client-side Blazor WebAssembly apps, <xref:Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader>. The <xref:Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader.LoadAssembliesAsync%2A?displayProperty=nameWithType> method:
2021-08-09 03:43:46 +08:00
* Uses [JS interop](xref:blazor/js-interop/call-dotnet-from-javascript) to fetch assemblies via a network call.
* Loads assemblies into the runtime executing on WebAssembly in the browser.
2023-09-07 23:53:28 +08:00
:::moniker range="< aspnetcore-8.0"
> [!NOTE]
> Guidance for *hosted* Blazor WebAssembly [solutions](xref:blazor/tooling#visual-studio-solution-file-sln) is covered in the [Lazy load assemblies in a hosted Blazor WebAssembly solution](#lazy-load-assemblies-in-a-hosted-blazor-webassembly-solution) section.
:::moniker-end
2021-08-09 03:43:46 +08:00
Blazor's <xref:Microsoft.AspNetCore.Components.Routing.Router> component designates the assemblies that Blazor searches for routable components and is also responsible for rendering the component for the route where the user navigates. The <xref:Microsoft.AspNetCore.Components.Routing.Router> component's [`OnNavigateAsync` method](xref:blazor/fundamentals/routing#handle-asynchronous-navigation-events-with-onnavigateasync) is used in conjunction with lazy loading to load the correct assemblies for endpoints that a user requests.
Logic is implemented inside <xref:Microsoft.AspNetCore.Components.Routing.Router.OnNavigateAsync> to determine the assemblies to load with <xref:Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader>. Options for how to structure the logic include:
* Conditional checks inside the <xref:Microsoft.AspNetCore.Components.Routing.Router.OnNavigateAsync> method.
* A lookup table that maps routes to assembly names, either injected into the component or implemented within the [`@code`](xref:mvc/views/razor#code) block.
In the following example:
* The namespace for <xref:Microsoft.AspNetCore.Components.WebAssembly.Services?displayProperty=fullName> is specified.
* The <xref:Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader> service is injected (`AssemblyLoader`).
* The `{PATH}` placeholder is the path where the list of assemblies should load. The example uses a conditional check for a single path that loads a single set of assemblies.
* The `{LIST OF ASSEMBLIES}` placeholder is the comma-separated list of assembly file name strings, including their file extensions (for example, `"Assembly1.{FILE EXTENSION}", "Assembly2.{FILE EXTENSION}"`).
2021-08-09 03:43:46 +08:00
`App.razor`:
:::moniker range=">= aspnetcore-6.0"
2021-08-09 03:43:46 +08:00
```razor
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger
2024-02-09 22:10:03 +08:00
<Router AppAssembly="typeof(App).Assembly"
OnNavigateAsync="OnNavigateAsync">
2021-08-09 03:43:46 +08:00
...
</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);
}
}
}
```
:::moniker-end
2021-08-09 03:43:46 +08:00
:::moniker range="< aspnetcore-6.0"
2021-08-09 03:43:46 +08:00
```razor
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger
2024-02-09 22:10:03 +08:00
<Router AppAssembly="typeof(Program).Assembly"
OnNavigateAsync="OnNavigateAsync">
2021-08-09 03:43:46 +08:00
...
</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);
}
}
}
```
:::moniker-end
2021-08-09 03:43:46 +08:00
> [!NOTE]
2023-11-29 23:36:21 +08:00
> The preceding example doesn't show the contents of the <xref:Microsoft.AspNetCore.Components.Routing.Router> component's Razor markup (`...`). For a demonstration with complete code, see the [Complete example](#complete-example) section of this article.
2021-08-09 03:43:46 +08:00
:::moniker range="= aspnetcore-5.0"
2021-08-09 03:43:46 +08:00
[!INCLUDE[](~/blazor/includes/prefer-exact-matches.md)]
2021-08-09 03:43:46 +08:00
:::moniker-end
2021-08-09 03:43:46 +08:00
## Assemblies that include routable components
2021-08-09 03:43:46 +08:00
2023-11-29 23:36:21 +08:00
When the list of assemblies includes routable components, the assembly list for a given path is passed to the <xref:Microsoft.AspNetCore.Components.Routing.Router> component's <xref:Microsoft.AspNetCore.Components.Routing.Router.AdditionalAssemblies> collection.
2021-08-09 03:43:46 +08:00
In the following example:
* The [List](xref:System.Collections.Generic.List%601)\<<xref:System.Reflection.Assembly>> in `lazyLoadedAssemblies` passes the assembly list to <xref:Microsoft.AspNetCore.Components.Routing.Router.AdditionalAssemblies>. The framework searches the assemblies for routes and updates the route collection if new routes are found. To access the <xref:System.Reflection.Assembly> type, the namespace for <xref:System.Reflection?displayProperty=fullName> is included at the top of the `App.razor` file.
2021-08-09 03:43:46 +08:00
* The `{PATH}` placeholder is the path where the list of assemblies should load. The example uses a conditional check for a single path that loads a single set of assemblies.
* The `{LIST OF ASSEMBLIES}` placeholder is the comma-separated list of assembly file name strings, including their file extensions (for example, `"Assembly1.{FILE EXTENSION}", "Assembly2.{FILE EXTENSION}"`).
2021-08-09 03:43:46 +08:00
`App.razor`:
:::moniker range=">= aspnetcore-6.0"
2021-08-09 03:43:46 +08:00
```razor
@using System.Reflection
2021-08-09 03:43:46 +08:00
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject ILogger<App> Logger
2023-12-04 21:24:41 +08:00
@inject LazyAssemblyLoader AssemblyLoader
2021-08-09 03:43:46 +08:00
2024-02-09 22:10:03 +08:00
<Router AppAssembly="typeof(App).Assembly"
AdditionalAssemblies="lazyLoadedAssemblies"
OnNavigateAsync="OnNavigateAsync">
2021-08-09 03:43:46 +08:00
...
</Router>
@code {
private List<Assembly> lazyLoadedAssemblies = new();
2021-08-09 03:43:46 +08:00
private async Task OnNavigateAsync(NavigationContext args)
{
try
{
if (args.Path == "{PATH}")
{
var assemblies = await AssemblyLoader.LoadAssembliesAsync(
new[] { {LIST OF ASSEMBLIES} });
lazyLoadedAssemblies.AddRange(assemblies);
2021-08-09 03:43:46 +08:00
}
}
catch (Exception ex)
{
Logger.LogError("Error: {Message}", ex.Message);
}
}
}
```
:::moniker-end
2021-08-09 03:43:46 +08:00
:::moniker range="< aspnetcore-6.0"
2021-08-09 03:43:46 +08:00
```razor
@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject ILogger<App> Logger
2023-12-04 21:24:41 +08:00
@inject LazyAssemblyLoader AssemblyLoader
2021-08-09 03:43:46 +08:00
2024-02-09 22:10:03 +08:00
<Router AppAssembly="typeof(Program).Assembly"
AdditionalAssemblies="lazyLoadedAssemblies"
OnNavigateAsync="OnNavigateAsync">
2021-08-09 03:43:46 +08:00
...
</Router>
@code {
private List<Assembly> lazyLoadedAssemblies = new List<Assembly>();
2021-08-09 03:43:46 +08:00
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);
}
}
}
```
:::moniker-end
2021-08-09 03:43:46 +08:00
> [!NOTE]
2023-11-29 23:36:21 +08:00
> The preceding example doesn't show the contents of the <xref:Microsoft.AspNetCore.Components.Routing.Router> component's Razor markup (`...`). For a demonstration with complete code, see the [Complete example](#complete-example) section of this article.
2021-08-09 03:43:46 +08:00
:::moniker range="= aspnetcore-5.0"
2021-08-13 21:31:50 +08:00
[!INCLUDE[](~/blazor/includes/prefer-exact-matches.md)]
2021-08-09 03:43:46 +08:00
:::moniker-end
2021-08-09 03:43:46 +08:00
For more information, see <xref:blazor/fundamentals/routing#route-to-components-from-multiple-assemblies>.
## User interaction with `<Navigating>` content
While loading assemblies, which can take several seconds, the <xref:Microsoft.AspNetCore.Components.Routing.Router> component can indicate to the user that a page transition is occurring with the router's <xref:Microsoft.AspNetCore.Components.Routing.Router.Navigating> property.
For more information, see <xref:blazor/fundamentals/routing#user-interaction-with-navigating-content>.
## Handle cancellations in `OnNavigateAsync`
The <xref:Microsoft.AspNetCore.Components.Routing.NavigationContext> object passed to the <xref:Microsoft.AspNetCore.Components.Routing.Router.OnNavigateAsync> callback contains a <xref:Microsoft.AspNetCore.Components.Routing.NavigationContext.CancellationToken> that's set when a new navigation event occurs. The <xref:Microsoft.AspNetCore.Components.Routing.Router.OnNavigateAsync> callback must throw when the cancellation token is set to avoid continuing to run the <xref:Microsoft.AspNetCore.Components.Routing.Router.OnNavigateAsync> callback on an outdated navigation.
2021-08-09 03:43:46 +08:00
For more information, see <xref:blazor/fundamentals/routing#handle-cancellations-in-onnavigateasync>.
## `OnNavigateAsync` events and renamed assembly files
2023-03-08 21:29:21 +08:00
The resource loader relies on the assembly names that are defined in the `blazor.boot.json` file. If [assemblies are renamed](xref:blazor/host-and-deploy/webassembly#change-the-file-name-extension-of-dll-files), the assembly names used in an <xref:Microsoft.AspNetCore.Components.Routing.Router.OnNavigateAsync> callback and the assembly names in the `blazor.boot.json` file are out of sync.
2021-08-09 03:43:46 +08:00
To rectify this:
* Check to see if the app is running in the `Production` environment when determining which assembly names to use.
* Store the renamed assembly names in a separate file and read from that file to determine what assembly name to use with the <xref:Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader> service and <xref:Microsoft.AspNetCore.Components.Routing.Router.OnNavigateAsync> callback.
2023-09-07 23:53:28 +08:00
:::moniker range="< aspnetcore-8.0"
2021-08-09 03:43:46 +08:00
## Lazy load assemblies in a hosted Blazor WebAssembly solution
The framework's lazy loading implementation supports lazy loading with prerendering in a hosted Blazor WebAssembly [solution](xref:blazor/tooling#visual-studio-solution-file-sln). During prerendering, all assemblies, including those marked for lazy loading, are assumed to be loaded. Manually register the <xref:Microsoft.AspNetCore.Components.WebAssembly.Services.LazyAssemblyLoader> service in the **:::no-loc text="Server":::** project.
2021-08-09 03:43:46 +08:00
2023-09-07 23:53:28 +08:00
:::moniker-end
:::moniker range=">= aspnetcore-6.0 < aspnetcore-8.0"
At the top of the `Program.cs` file of the **:::no-loc text="Server":::** project, add the namespace for <xref:Microsoft.AspNetCore.Components.WebAssembly.Services?displayProperty=fullName>:
```csharp
using Microsoft.AspNetCore.Components.WebAssembly.Services;
```
In `Program.cs` of the **:::no-loc text="Server":::** project, register the service:
```csharp
builder.Services.AddScoped<LazyAssemblyLoader>();
```
:::moniker-end
:::moniker range="< aspnetcore-6.0"
At the top of the `Startup.cs` file of the **:::no-loc text="Server":::** project, add the namespace for <xref:Microsoft.AspNetCore.Components.WebAssembly.Services?displayProperty=fullName>:
2021-08-09 03:43:46 +08:00
```csharp
using Microsoft.AspNetCore.Components.WebAssembly.Services;
```
In `Startup.ConfigureServices` (`Startup.cs`) of the **:::no-loc text="Server":::** project, register the service:
2021-08-09 03:43:46 +08:00
```csharp
services.AddScoped<LazyAssemblyLoader>();
```
:::moniker-end
2021-08-09 03:43:46 +08:00
## Complete example
The demonstration in this section:
* Creates a robot controls assembly (`GrantImaharaRobotControls.{FILE EXTENSION}`) as a [Razor class library (RCL)](xref:blazor/components/class-libraries) that includes a `Robot` component (`Robot.razor` with a route template of `/robot`).
2021-08-09 03:43:46 +08:00
* Lazily loads the RCL's assembly to render its `Robot` component when the `/robot` URL is requested by the user.
Create a standalone Blazor WebAssembly app to demonstrate lazy loading of a Razor class library's assembly. Name the project `LazyLoadTest`.
Add an ASP.NET Core class library project to the solution:
2021-08-09 03:43:46 +08:00
* Visual Studio: Right-click the solution file in **Solution Explorer** and select **Add** > **New project**. From the dialog of new project types, select **Razor Class Library**. Name the project `GrantImaharaRobotControls`. Do **not** select the **Support pages and view** checkbox.
* Visual Studio Code/.NET CLI: Execute `dotnet new razorclasslib -o GrantImaharaRobotControls` from a command prompt. The `-o|--output` option creates a folder and names the project `GrantImaharaRobotControls`.
2021-08-09 03:43:46 +08:00
The example component presented later in this section uses a [Blazor form](xref:blazor/forms/index). In the RCL project, add the [`Microsoft.AspNetCore.Components.Forms`](https://www.nuget.org/packages/Microsoft.AspNetCore.Components.Forms) package to the project.
2021-08-09 03:43:46 +08:00
[!INCLUDE[](~/includes/package-reference.md)]
2021-08-09 03:43:46 +08:00
Create a `HandGesture` class in the RCL with a `ThumbUp` method that hypothetically makes a robot perform a thumbs-up gesture. The method accepts an argument for the axis, `Left` or `Right`, as an [`enum`](/dotnet/csharp/language-reference/builtin-types/enum). The method returns `true` on success.
2021-08-09 03:43:46 +08:00
`HandGesture.cs`:
2021-08-09 03:43:46 +08:00
2023-02-17 20:05:04 +08:00
:::moniker range=">= aspnetcore-6.0"
```csharp
using Microsoft.Extensions.Logging;
2023-02-17 20:05:04 +08:00
namespace GrantImaharaRobotControls;
2023-02-17 20:05:04 +08:00
public static class HandGesture
{
public static bool ThumbUp(Axis axis, ILogger logger)
{
logger.LogInformation("Thumb up gesture. Axis: {Axis}", axis);
2023-02-17 20:05:04 +08:00
// Code to make robot perform gesture
2023-02-17 20:05:04 +08:00
return true;
}
}
2023-02-17 20:05:04 +08:00
public enum Axis { Left, Right }
```
2023-02-17 20:05:04 +08:00
:::moniker-end
:::moniker range="< aspnetcore-6.0"
```csharp
using Microsoft.Extensions.Logging;
2021-08-09 03:43:46 +08:00
namespace GrantImaharaRobotControls
{
public static class HandGesture
{
public static bool ThumbUp(Axis axis, ILogger logger)
{
logger.LogInformation("Thumb up gesture. Axis: {Axis}", axis);
2021-08-09 03:43:46 +08:00
// Code to make robot perform gesture
2021-08-09 03:43:46 +08:00
return true;
}
}
2021-08-09 03:43:46 +08:00
public enum Axis { Left, Right }
}
```
2021-08-09 03:43:46 +08:00
2023-02-17 20:05:04 +08:00
:::moniker-end
Add the following component to the root of the RCL project. The component permits the user to submit a left or right hand thumb-up gesture request.
2021-08-09 03:43:46 +08:00
`Robot.razor`:
2021-08-09 03:43:46 +08:00
:::moniker range=">= aspnetcore-8.0"
```razor
@page "/robot"
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.Extensions.Logging
@inject ILogger<Robot> Logger
<h1>Robot</h1>
<EditForm FormName="RobotForm" Model="robotModel" OnValidSubmit="HandleValidSubmit">
<InputRadioGroup @bind-Value="robotModel.AxisSelection">
@foreach (var entry in Enum.GetValues<Axis>())
{
<InputRadio Value="entry" />
<text>&nbsp;</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; }
}
}
```
:::moniker-end
:::moniker range=">= aspnetcore-6.0 < aspnetcore-8.0"
```razor
@page "/robot"
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.Extensions.Logging
@inject ILogger<Robot> Logger
2021-08-09 03:43:46 +08:00
<h1>Robot</h1>
2021-08-09 03:43:46 +08:00
2024-02-09 22:10:03 +08:00
<EditForm Model="robotModel" OnValidSubmit="HandleValidSubmit">
<InputRadioGroup @bind-Value="robotModel.AxisSelection">
@foreach (var entry in Enum.GetValues<Axis>())
{
2024-02-09 22:10:03 +08:00
<InputRadio Value="entry" />
<text>&nbsp;</text>@entry<br>
}
</InputRadioGroup>
2021-08-09 03:43:46 +08:00
<button type="submit">Submit</button>
</EditForm>
2021-08-09 03:43:46 +08:00
<p>
@message
</p>
2021-08-09 03:43:46 +08:00
@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; }
}
}
```
:::moniker-end
:::moniker range="< aspnetcore-6.0"
```razor
@page "/robot"
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.Extensions.Logging
@inject ILogger<Robot> Logger
<h1>Robot</h1>
2024-02-09 22:10:03 +08:00
<EditForm Model="robotModel" OnValidSubmit="HandleValidSubmit">
<InputRadioGroup @bind-Value="robotModel.AxisSelection">
@foreach (var entry in (Axis[])Enum
.GetValues(typeof(Axis)))
{
2024-02-09 22:10:03 +08:00
<InputRadio Value="entry" />
<text>&nbsp;</text>@entry<br>
}
</InputRadioGroup>
<button type="submit">Submit</button>
</EditForm>
<p>
@message
</p>
@code {
private RobotModel robotModel = new RobotModel() { AxisSelection = Axis.Left };
private string message;
2021-08-09 03:43:46 +08:00
private void HandleValidSubmit()
{
Logger.LogInformation("HandleValidSubmit called");
2021-08-09 03:43:46 +08:00
var result = HandGesture.ThumbUp(robotModel.AxisSelection, Logger);
2021-08-09 03:43:46 +08:00
message = $"ThumbUp returned {result} at {DateTime.Now}.";
}
2021-08-09 03:43:46 +08:00
public class RobotModel
{
public Axis AxisSelection { get; set; }
}
}
```
2021-08-09 03:43:46 +08:00
:::moniker-end
In the `LazyLoadTest` project, create a project reference for the `GrantImaharaRobotControls` RCL:
2021-08-09 03:43:46 +08:00
* Visual Studio: Right-click the `LazyLoadTest` project and select **Add** > **Project Reference** to add a project reference for the `GrantImaharaRobotControls` RCL.
* Visual Studio Code/.NET CLI: Execute `dotnet add reference {PATH}` in a command shell from the project's folder. The `{PATH}` placeholder is the path to the RCL project.
2021-08-09 03:43:46 +08:00
Specify the RCL's assembly for lazy loading in the `LazyLoadTest` app's project file (`.csproj`):
2021-08-09 03:43:46 +08:00
```xml
<ItemGroup>
<BlazorWebAssemblyLazyLoad Include="GrantImaharaRobotControls.{FILE EXTENSION}" />
</ItemGroup>
```
2021-08-09 03:43:46 +08:00
The following <xref:Microsoft.AspNetCore.Components.Routing.Router> component demonstrates loading the `GrantImaharaRobotControls.{FILE EXTENSION}` assembly when the user navigates to `/robot`. Replace the app's default `App` component with the following `App` component.
2021-08-09 03:43:46 +08:00
During page transitions, a styled message is displayed to the user with the `<Navigating>` element. For more information, see the [User interaction with `<Navigating>` content](#user-interaction-with-navigating-content) section.
2021-08-09 03:43:46 +08:00
The assembly is assigned to <xref:Microsoft.AspNetCore.Components.Routing.Router.AdditionalAssemblies>, which results in the router searching the assembly for routable components, where it finds the `Robot` component. The `Robot` component's route is added to the app's route collection. For more information, see the <xref:blazor/fundamentals/routing#route-to-components-from-multiple-assemblies> article and the [Assemblies that include routable components](#assemblies-that-include-routable-components) section of this article.
2021-08-09 03:43:46 +08:00
`App.razor`:
2021-08-09 03:43:46 +08:00
:::moniker range=">= aspnetcore-6.0"
2021-08-09 03:43:46 +08:00
```razor
@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject ILogger<App> Logger
2023-12-04 21:24:41 +08:00
@inject LazyAssemblyLoader AssemblyLoader
2021-08-09 03:43:46 +08:00
2024-02-09 22:10:03 +08:00
<Router AppAssembly="typeof(App).Assembly"
AdditionalAssemblies="lazyLoadedAssemblies"
OnNavigateAsync="OnNavigateAsync">
<Navigating>
<div style="padding:20px;background-color:blue;color:white">
<p>Loading the requested page&hellip;</p>
</div>
</Navigating>
<Found Context="routeData">
2024-02-09 22:10:03 +08:00
<RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" />
</Found>
<NotFound>
2024-02-09 22:10:03 +08:00
<LayoutView Layout="typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
2021-08-09 03:43:46 +08:00
@code {
private List<Assembly> lazyLoadedAssemblies = new();
private async Task OnNavigateAsync(NavigationContext args)
{
try
{
if (args.Path == "robot")
{
var assemblies = await AssemblyLoader.LoadAssembliesAsync(
new[] { "GrantImaharaRobotControls.{FILE EXTENSION}" });
lazyLoadedAssemblies.AddRange(assemblies);
}
}
catch (Exception ex)
{
Logger.LogError("Error: {Message}", ex.Message);
}
}
}
```
2021-08-09 03:43:46 +08:00
:::moniker-end
:::moniker range="< aspnetcore-6.0"
```razor
@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@using Microsoft.Extensions.Logging
@inject ILogger<App> Logger
2023-12-04 21:24:41 +08:00
@inject LazyAssemblyLoader AssemblyLoader
2024-02-09 22:10:03 +08:00
<Router AppAssembly="typeof(Program).Assembly"
AdditionalAssemblies="lazyLoadedAssemblies"
OnNavigateAsync="OnNavigateAsync">
<Navigating>
<div style="padding:20px;background-color:blue;color:white">
<p>Loading the requested page&hellip;</p>
</div>
</Navigating>
<Found Context="routeData">
2024-02-09 22:10:03 +08:00
<RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" />
</Found>
<NotFound>
2024-02-09 22:10:03 +08:00
<LayoutView Layout="typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
@code {
private List<Assembly> lazyLoadedAssemblies = new List<Assembly>();
2021-08-09 03:43:46 +08:00
private async Task OnNavigateAsync(NavigationContext args)
{
try
{
if (args.Path == "robot")
{
var assemblies = await AssemblyLoader.LoadAssembliesAsync(
new[] { "GrantImaharaRobotControls.{FILE EXTENSION}" });
lazyLoadedAssemblies.AddRange(assemblies);
}
}
catch (Exception ex)
{
Logger.LogError("Error: {Message}", ex.Message);
}
}
}
```
2021-08-09 03:43:46 +08:00
:::moniker-end
2021-08-09 03:43:46 +08:00
Build and run the app.
2021-08-09 03:43:46 +08:00
When the `Robot` component from the RCL is requested at `/robot`, the `GrantImaharaRobotControls.{FILE EXTENSION}` assembly is loaded and the `Robot` component is rendered. You can inspect the assembly loading in the **Network** tab of the browser's developer tools.
2021-08-09 03:43:46 +08:00
## Troubleshoot
* If unexpected rendering occurs, such as rendering a component from a previous navigation, confirm that the code throws if the cancellation token is set.
* If assemblies configured for lazy loading unexpectedly load at app start, check that the assembly is marked for lazy loading in the project file.
:::moniker range="< aspnetcore-6.0"
2021-08-09 03:43:46 +08:00
> [!NOTE]
> A known issue exists for loading types from a lazily-loaded assembly. For more information, see [Blazor WebAssembly lazy loading assemblies not working when using @ref attribute in the component (dotnet/aspnetcore #29342)](https://github.com/dotnet/aspnetcore/issues/29342).
:::moniker-end
2021-08-09 03:43:46 +08:00
## Additional resources
* [Handle asynchronous navigation events with `OnNavigateAsync`](xref:blazor/fundamentals/routing#handle-asynchronous-navigation-events-with-onnavigateasync)
* <xref:blazor/performance>