Blazor WebAssembly, Teil 4: Zustandsverwaltung und Nachladen von Modulen
Seite 3: Modularisierung und Lazy Loading
Die bisher erstellte Blazor-WebAssembly-Anwendung ist nicht sehr groß, aber lädt dennoch schon rund 70 Dateien und rund 10 MByte in den Webbrowser. Schon seit der ersten Version Blazor WebAssembly 3.2 ist es möglich, Razor Components und sonstige C#-Klassen sowie auch klassische Webinhalte wie HTML-, CSS-, JavaScript- und Grafikdateien in sogenannte Razor Class Libraries (RCL) auszulagern. Das diente bisher aber nur der Wiederverwendbarkeit und nicht dem Nachladen bei Bedarf (Lazy Loading). Diese schmerzlich vermisste Funktion hat Microsoft in Blazor 5.0 nachgerüstet: Damit ist es möglich, die Downloadlast beim Start der Anwendung zu reduzieren.
In Fall des MiracleList-Frontends soll bei Anwendungsstart nur noch die Anmeldemaske geladen werden. Alle andere Razor Components sollen erst nach dem erfolgreichen Anmelden vom Webserver geholt werden. Weder Microsofts Standardprojektvorlage noch das bisher erstellte MiracleList-Projekt sind auf Lazy Loading vorkonfiguriert. Um das zu integrieren, sind folgende Schritte notwendig:
- Anlegen eines weiteren Projekts vom Typ "Razor Class Library" mit Namen "MiracleListRCL" in der Projektmappe "MiracleList". Dazu nutzen Entwickler im Kontextmenü der Projektmappe Add | New Project | Razor Class Library. Wichtig ist, dass sie ".NET 5.0" als Target Framework wählen (und dafür Visual Studio 2019 ab Version 16.8 verwenden), aber "Support pages and views" nicht wählen, denn damit würde man eine serverseitige Razor Class Library für ASP.NET Core MVC und ASP.NET Core Razor Pages erschaffen. Entwickler sollten in MiracleListRCL.csproj
<Project Sdk="Microsoft.NET.Sdk.Razor">
und<TargetFramework>net5.0</TargetFramework>
sowie<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="5.0.x" />
sehen. - Erstellen einer weiteren, normalen .NET-5.0-Klassenbibliothek (
<Project Sdk="Microsoft.NET.Sdk">
, (<TargetFramework>net5.0</TargetFramework>(
) mit Namen "MiracleListUtil". Das ist als Heimat fĂĽr die KlasseAuthenticationManager.cs
notwendig, denn diese muss sowohl im Startprojekt als auch in der nachzuladenden MiracleListRCL.dll verfügbar sein. - Erstellen einer Referenz in MiracleListUtil.csproj und MiracleListRCL.csproj auf die Projekte "BO" und "MiracleListAPI_Proxy" (das geht in Visual Studio 2019 mittlerweile per Drag & Drop des Projekts auf den Ast "Dependencies"), denn beide DLLs müssen auf das Backend zugreifen.
- Löschen der vordefinierten Dateien "Component1.razor", "ExampleJsInterop.cs" und des Inhalt des Ordners /wwwroot in MiracleListRCL.csproj.
- Anlegen eines Ordners "/Pages" in MiracleListRCL.csproj.
- Den Namen "Web" in MiracleListRCL.csproj festlegen, und zwar in den Projekteigenschaften in der Registerkarte "Application" als "Default Namespace". Das erlaubt, den bisherigen Programmcode ohne Namensraumänderungen hierher zu verschieben.
- Verschieben der Razor Components "About.razor", "Index.Razor" und "TaskEdit.razor" per Drag & Drop mit gedrückter Umschalt-Taste von MiracleListBW.csproj nach MiracleListRCL.csproj in den Ordner "/Pages". Die Razor Components müssen in den Unterordner "/Pages", damit die Namensräume passen.
- Verschieben der Klasse AuthenticationManager.cs von MiracleListBW.csproj nach MiracleListUtil.csproj
- Erstellen einer Referenz von MiracleListBW.csproj zu MiracleListRCL.csproj und MiracleListUtil.csproj, damit die Blazor-WebAssembly-Anwendung wieder alle bisher bekannten Teile kennt.
- Erstellen einer Referenz von MiracleListRCL.csproj zu MiracleListUtil.csproj, damit die nachzuladende DLL den
AuthenticationManager
kennt. - In MiracleListUtil.csproj die gleiche Version des NuGet-Pakets "Microsoft.AspNetCore.Components.Authorization" installieren, das in Teil 3 des Tutorials auch im Blazor-WebAssembly-Projekt installiert wurde, zum Beispiel Install-Package Microsoft.AspNetCore.Components.Authorization -Version 5.0.2
- Die gleiche Version des NuGet-Pakets "Blazored.LocalStorage" in MiracleListUtil.csproj installieren, das im Teil 3 auch im Blazor-WebAssembly-Projekt installiert wurde.
- Entfernen der Referenz auf "Blazored.LocalStorage" aus MiracleListBW.csproj.
- Den Namen der nachzuladenden DLL mit ".dll" am Ende in der Projektdatei (.csproj) des Blazor-WebAssembly-Projekts MiracleListBW.csproj ergänzen. (Achtung: Vor der RC1-Version war hier die Angabe von .dll verboten.)
<ItemGroup>
<BlazorWebAssemblyLazyLoad Include="MiracleListRCL.dll" />
</ItemGroup>
- Die App.razor-Datei im Blazor-WebAssembly-Projekt mit dem Inhalt des Listings 3 ersetzen. Die Klasse LazyAssemblyLoader und das Ereignis
OnNavigateAsync()
sind neu in Blazor 5.0. Normalerweise würde bei AdditionalAssemblies ein fester Eintrag (z. B.new[] { typeof(Web.Pages.Index).Assembly }
) stehen. Nun ist es die Property, die zur Laufzeit im EreignisOnNavigateAsync()
verändert wird, falls die URL auf main oder about endet.
@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@inject LazyAssemblyLoader LazyAssemblyLoader
<CascadingAuthenticationState>
<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="lazyLoadedAssemblies" OnNavigateAsync="@OnNavigateAsync">
<Navigating>
<div>
<p>Loading the requested page...</p>
</div>
</Navigating>
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData"
DefaultLayout="@typeof(MainLayout)">
<NotAuthorized>
Sie sind nicht angemeldet und können den Inhalt dieser Seite daher nicht sehen. Bitte melden Sie sich an in der <a href="/Login">Anmeldenmaske</a>
@{
NavigationManager.NavigateTo("/");
}
</NotAuthorized>
</AuthorizeRouteView>
</Found>
<NotFound>
<LayoutView Layout="typeof(MainLayout)">
<p>Sorry, there is nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
@code {
private List<Assembly> lazyLoadedAssemblies =
new List<Assembly>();
private async Task OnNavigateAsync(NavigationContext args)
{
Console.WriteLine("OnNavigateAsync: " + args.Path);
if (args.Path.EndsWith("main") || args.Path.EndsWith("about"))
{
Console.WriteLine("Lazy Loading MiracleListRCL.dll...");
var assemblies = await LazyAssemblyLoader.LoadAssembliesAsync(new string[] { "MiracleListRCL.dll" });
lazyLoadedAssemblies.AddRange(assemblies);
}
}
}
Listing 3: App.razor mit Lazy Loading
Nach diesen umfangreichen Änderungen sollte die MiracleList-Projektmappe so aussehen, wie sie Abbildung 2 zeigt. Die Anwendung verhält sich beim Start wie bekannt. Nur wenn man in die Netzwerkansicht der Entwicklerwerkzeuge im Browser schaut, sieht man, dass MiracleListRCL.dll erst nach der Benutzeranmeldung nachgeladen wird. Der Einspareffekt beim Anwendungsstart ist in diesem Fall nicht groß, weil die MiracleListRCL.dll nur rund 13 KByte groß ist. Aber wenn die Anzahl der Razor Components in der Webanwendung wächst, vergrößert sich der Effekt. Zudem kann man durch einen möglichst spartanischen Anmeldedialog auch Grafiken unter anderem Ressourcen erst später zu laden.