Är din Blazor-app långsam? 7 orsaker och hur du åtgärdar dem
Laggar Blazor-appen vid varje klick, fryser en tabell eller växer serverns minne hela tiden? De sju vanligaste orsakerna, med kod före och efter, för .NET 10.
Vlado Pandžić · Grundare · Senior .NET-arkitekt
Publicerad · 4 min läsning
I början flyger en Blazor-applikation. Några få skärmar, lite data, allt svarar direkt. Ett år senare laggar ett knapptryck, ordertabellen fryser i ett par sekunder och servern använder mer och mer minne ju längre dagen går.
Blazor är sällan ensam boven. Det är nästan alltid en av de här sju orsakerna, och alla går att åtgärda utan att skriva om applikationen. Varje orsak finns här med kod, för .NET 10.
Först: mät vad som renderas
Den vanligaste överraskningen är hur många gånger en komponent renderas. En tillfällig logg visar det på en minut:
protected override void OnAfterRender(bool firstRender)
=> Logger.LogDebug("{Component} rendered", GetType().Name); // temporary, only while measuring
Om ett klick ger femtio rader i loggen har du hittat problem nummer ett.
1. Allt renderas om vid varje klick
Blazor renderar om en underkomponent när dess parametrar ändras. Men när en parameter är ett komplext objekt, till exempel en hel order, kan Blazor inte avgöra om något inuti har ändrats, så den renderas om varje gång. Enkla typer som int och string renderar inte om komponenten förrän värdet faktiskt ändras.
@* before: the whole object, so the row re-renders on every parent change *@
<OrderRow Order="order" />
@* after: only what the row displays *@
<OrderRow Number="@order.Number" Customer="@order.CustomerName" Total="@order.Total" />
När det inte går kan komponenten själv avgöra om den ska renderas:
private int lastVersion;
protected override bool ShouldRender()
{
var changed = Order.Version != lastVersion; // render only when the order really changed
lastVersion = Order.Version;
return changed;
}
2. Listor utan @key
När en lista ändras jämför Blazor utan @key raderna efter position. En rad som läggs till överst gör att alla rader under den renderas om, och tillstånd inuti raderna kan hamna i fel rad.
@foreach (var order in orders)
{
<OrderRow @key="order.Id" Number="@order.Number" Total="@order.Total" />
}
3. Tabeller med tusentals rader
Femtusen rader som renderas på en gång betyder femtusen komponenter i minnet och på skärmen, fast användaren bara ser tjugo av dem. Virtualize renderar bara det som syns:
<Virtualize Items="orders" Context="order">
<OrderRow @key="order.Id" Number="@order.Number" Total="@order.Total" />
</Virtualize>
QuickGrid gör samma sak med Virtualize="true", och för riktigt stora datamängder kan båda hämta rader från servern bit för bit.
4. Data läses in två gånger
Med förrendering körs OnInitializedAsync två gånger: en gång på servern för den första visningen och en gång till när komponenten blir interaktiv. Det betyder två identiska databasfrågor och ett flimmer på skärmen. I .NET 10 löser ett attribut det:
@code {
[PersistentState]
public List<OrderSummary>? Orders { get; set; }
protected override async Task OnInitializedAsync()
{
Orders ??= await OrderService.GetOpenAsync(); // the second time, the data is already there
}
}
5. StateHasChanged för ofta
En komponent som följer priser, statusar eller meddelanden i realtid hamnar lätt på dussintals renderingar per sekund. Användaren ser ingen skillnad mellan 4 och 40 uppdateringar per sekund, men servern och webbläsaren märker den tydligt.
protected override void OnInitialized()
{
Prices.Changed += OnPriceChanged;
renderTimer = new Timer(_ =>
{
if (!dirty) return;
dirty = false;
InvokeAsync(StateHasChanged); // at most four renders a second
}, null, 250, 250);
}
private void OnPriceChanged(object? sender, PriceChangedEventArgs e) => dirty = true; // just note the change
6. Serverminne som bara växer
En komponent som prenumererar på en händelse eller startar en timer och aldrig avslutar den dör aldrig. På Blazor Server betyder det att minnet växer med varje användare och varje öppen skärm, fram till nästa omstart.
@implements IDisposable
@code {
public void Dispose()
{
Prices.Changed -= OnPriceChanged; // without this, the component stays in memory
renderTimer?.Dispose();
}
}
7. Fel renderingsläge för skärmen
I .NET 10 kan varje sida ha sitt eget renderingsläge. Interactive Server passar utmärkt för interna skärmar på ett bra nätverk, men på en dålig uppkoppling väntar varje klick på nätverket. Sidor som bara läses behöver ingen interaktivitet alls.
@* MonthlyReport.razor: read only, static rendering with no connection to the server *@
@page "/reports/monthly"
@* Orders.razor: an interactive screen *@
@page "/orders"
@rendermode InteractiveServer
Vilket läge som passar vilken skärm står i tabellen på sidan om vår Blazor-specialisering.
I vilken ordning
- MätVad som renderas och hur ofta
- Dela uppMindre komponenter, enkla parametrar
- VirtualiseraStora listor och tabeller
- StädaDispose och renderingsläget
Ofta är en långsam Blazor-skärm inte alls ett Blazor-problem, utan ett problem med frågorna bakom den. Om en skärm tar lång tid att ladda men renderar lite, läs artikeln om EF Core-prestanda.
Så arbetar vi
ProCoding är en .NET-studio från Split i Kroatien, och vi har levererat Blazor i produktion sedan 2020, från programvara som används för att bygga elbilar till styrning av batterilager. Det här är precis vad vi gör: en Blazor-hälsokontroll visar vilka av de här orsakerna som gör din applikation långsam och ger dig en prioriterad lista med åtgärder. Det första steget är ett kostnadsfritt samtal på 30 minuter.
Källor
- Metodtips för prestanda i ASP.NET Core Blazor, Microsoft Learn
- Renderingsprestanda i Blazor, Microsoft Learn
- Virtualisering av Blazor-komponenter, Microsoft Learn
- Beständigt tillstånd vid förrendering, Microsoft Learn
- Frigöra komponenter (Dispose), Microsoft Learn
Den här artikeln är endast allmän information och inte juridisk, skattemässig, ekonomisk eller annan professionell rådgivning. Scenarier, exempel och beräkningar är illustrativa. Användarvillkor och ansvarsfriskrivning.