Is uw Blazor-app traag? 7 oorzaken en hoe u ze oplost
Uw Blazor-app hapert bij elke klik, een tabel bevriest of het servergeheugen groeit? De zeven meest voorkomende oorzaken, met code voor en na.
Vlado Pandžić · Oprichter · Senior .NET-architect
Gepubliceerd · 4 min lezen
In het begin vliegt een Blazor-applicatie. Een paar schermen, weinig data, alles reageert meteen. Een jaar later hapert een klik op een knop, bevriest de ordertabel een paar seconden, en gebruikt de server in de loop van de dag steeds meer geheugen.
Blazor is zelden alleen de schuldige. Het is bijna altijd een van deze zeven oorzaken, en ze zijn allemaal op te lossen zonder de applicatie te herschrijven. Elke oorzaak staat hier met code, voor .NET 10.
Eerst: meet wat er rendert
De meest voorkomende verrassing is hoe vaak een component rendert. Een tijdelijke log laat het in een minuut zien:
protected override void OnAfterRender(bool firstRender)
=> Logger.LogDebug("{Component} rendered", GetType().Name); // tijdelijk, alleen tijdens het meten
Zet één klik vijftig regels in de log, dan hebt u probleem nummer één gevonden.
1. Alles rendert opnieuw bij elke klik
Blazor rendert een kind opnieuw als zijn parameters veranderen. Maar is een parameter een complex object, zoals een hele order, dan kan Blazor niet weten of daarin iets is veranderd, en rendert het elke keer. Eenvoudige types zoals int en string laten de component pas opnieuw renderen als hun waarde verandert.
@* voor: het hele object, dus de rij rendert bij elke wijziging van de ouder *@
<OrderRow Order="order" />
@* na: alleen wat de rij toont *@
<OrderRow Number="@order.Number" Customer="@order.CustomerName" Total="@order.Total" />
Als dat niet kan, kan de component zelf bepalen of hij rendert:
private int lastVersion;
protected override bool ShouldRender()
{
var changed = Order.Version != lastVersion; // alleen renderen als de order echt is veranderd
lastVersion = Order.Version;
return changed;
}
2. Lijsten zonder @key
Als een lijst verandert, vergelijkt Blazor zonder @key de rijen op positie. Eén rij die bovenaan wordt ingevoegd, betekent dat alle rijen eronder opnieuw renderen, en toestand binnen de rijen kan in de verkeerde rij belanden.
@foreach (var order in orders)
{
<OrderRow @key="order.Id" Number="@order.Number" Total="@order.Total" />
}
3. Tabellen met duizenden rijen
Vijfduizend rijen in één keer gerenderd betekent vijfduizend componenten in het geheugen en op het scherm, terwijl de gebruiker er twintig ziet. Virtualize rendert alleen wat zichtbaar is:
<Virtualize Items="orders" Context="order">
<OrderRow @key="order.Id" Number="@order.Number" Total="@order.Total" />
</Virtualize>
QuickGrid doet hetzelfde met Virtualize="true", en voor echt grote hoeveelheden data kunnen beide de rijen stukje bij beetje van de server ophalen.
4. Data wordt twee keer geladen
Met prerendering draait OnInitializedAsync twee keer: één keer op de server voor de eerste weergave, en nog een keer als de component interactief wordt. Dat betekent twee identieke databasequeries en een flikkering op het scherm. In .NET 10 lost één attribuut dat op:
@code {
[PersistentState]
public List<OrderSummary>? Orders { get; set; }
protected override async Task OnInitializedAsync()
{
Orders ??= await OrderService.GetOpenAsync(); // de tweede keer is de data er al
}
}
5. StateHasChanged te vaak
Een component die prijzen, statussen of berichten in realtime volgt, komt al snel op tientallen renders per seconde. De gebruiker ziet geen verschil tussen 4 en 40 verversingen per seconde, maar de server en de browser wel degelijk.
protected override void OnInitialized()
{
Prices.Changed += OnPriceChanged;
renderTimer = new Timer(_ =>
{
if (!dirty) return;
dirty = false;
InvokeAsync(StateHasChanged); // hooguit vier renders per seconde
}, null, 250, 250);
}
private void OnPriceChanged(object? sender, PriceChangedEventArgs e) => dirty = true; // alleen de wijziging noteren
6. Servergeheugen dat alleen maar groeit
Een component die zich abonneert op een event of een timer start en dat nooit opzegt, sterft nooit. Op Blazor Server betekent dat dat het geheugen groeit met elke gebruiker en elk geopend scherm, tot de volgende herstart.
@implements IDisposable
@code {
public void Dispose()
{
Prices.Changed -= OnPriceChanged; // zonder dit blijft de component in het geheugen
renderTimer?.Dispose();
}
}
7. De verkeerde rendermodus voor het scherm
In .NET 10 kan elke pagina een eigen rendermodus hebben. Interactive Server is uitstekend voor interne schermen op een goed netwerk, maar op een slechte verbinding wacht elke klik op het netwerk. Pagina’s die alleen worden gelezen, hebben helemaal geen interactiviteit nodig.
@* MonthlyReport.razor: alleen lezen, statisch renderen zonder verbinding met de server *@
@page "/reports/monthly"
@* Orders.razor: een interactief scherm *@
@page "/orders"
@rendermode InteractiveServer
Welke modus voor welk scherm staat in de tabel op de pagina Blazor-specialisatie.
In welke volgorde
- MetenHoe vaak wat rendert
- OpsplitsenKleinere componenten, eenvoudige parameters
- VirtualiserenGrote lijsten en tabellen
- OpruimenDispose en de rendermodus
Vaak is een traag Blazor-scherm helemaal geen Blazor-probleem, maar een probleem met de queries erachter. Laadt een scherm lang maar rendert het weinig, lees dan het artikel over EF Core-performance.
Hoe wij werken
ProCoding is een .NET-studio uit Split, Kroatië, en we brengen Blazor sinds 2020 in productie, van software voor het bouwen van elektrische auto’s tot het aansturen van batterijopslag. Een Blazor health check vindt welke van deze oorzaken uw applicatie afremt en levert een geprioriteerde lijst met verbeteringen. De eerste stap is een gratis gesprek van 30 minuten.
Bronnen
- Best practices voor prestaties van ASP.NET Core Blazor, Microsoft Learn
- Renderprestaties van Blazor, Microsoft Learn
- Virtualisatie van Blazor-componenten, Microsoft Learn
- Persistentie van vooraf gerenderde status, Microsoft Learn
- Componenten verwijderen, Microsoft Learn
Dit artikel is alleen algemene informatie en geen juridisch, fiscaal, financieel of ander professioneel advies. Scenario’s, voorbeelden en berekeningen zijn ter illustratie. Gebruiksvoorwaarden en disclaimer.