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

644 lines
25 KiB
Markdown
Raw 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
2023-11-15 02:56:15 +08:00
ms.date: 11/14/2023
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 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.
## 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
2022-09-02 03:11:38 +08:00
<Router AppAssembly="@typeof(App).Assembly"
2021-08-09 03:43:46 +08:00
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);
}
}
}
```
:::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
<Router AppAssembly="@typeof(Program).Assembly"
2021-08-09 03:43:46 +08:00
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);
}
}
}
```
:::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 LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger
<Router AppAssembly="@typeof(App).Assembly"
AdditionalAssemblies="@lazyLoadedAssemblies"
2021-08-09 03:43:46 +08:00
OnNavigateAsync="@OnNavigateAsync">
...
</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 LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger
<Router AppAssembly="@typeof(Program).Assembly"
AdditionalAssemblies="@lazyLoadedAssemblies"
OnNavigateAsync="@OnNavigateAsync">
...
</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-6.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
<EditForm Model="@robotModel" OnValidSubmit="@HandleValidSubmit">
<InputRadioGroup @bind-Value="robotModel.AxisSelection">
@foreach (var entry in (Axis[])Enum
.GetValues(typeof(Axis)))
{
<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>
<EditForm Model="@robotModel" OnValidSubmit="@HandleValidSubmit">
<InputRadioGroup @bind-Value="robotModel.AxisSelection">
@foreach (var entry in (Axis[])Enum
.GetValues(typeof(Axis)))
{
<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 LazyAssemblyLoader AssemblyLoader
@inject ILogger<App> Logger
2021-08-09 03:43:46 +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">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<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 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&hellip;</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 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>