Alla render modes på djupet, prerendering, prestandaverktyg och hur du publicerar appen till Azure App Service eller IIS.
| Mode | Första laddning | Interaktivitet | Krav på server |
|---|---|---|---|
| Static SSR | Mycket snabb (ren HTML) | Ingen (formulär funkar via POST) | Bara HTTP-svar |
| Server | Snabb (liten HTML + JS) | Omedelbar, kräver SignalR | Konstant WebSocket-uppkoppling |
| WebAssembly | Långsam (laddar .NET-runtime) | Omedelbar efter laddning | Statisk filserver räcker |
| Auto | Snabb som Server | Omedelbar, byter till WASM | Server + statiska filer |
@page "/counter"
@rendermode InteractiveServer
<h1>Räknare</h1>
<button @onclick="() => count++">@count</button>
@code { private int count; }
Tillgängliga värden:
InteractiveServerInteractiveWebAssemblyInteractiveAutoDu kan också sätta render mode när du använder en komponent:
<Counter @rendermode="InteractiveWebAssembly" />
Som standard prerenderas även interaktiva komponenter — d.v.s. servern renderar dem en gång till HTML innan de blir interaktiva. Det ger snabbare uppfattad laddning och bättre SEO, men har två fällor:
OnInitializedAsync körs både på servern och sedan igen när komponenten blir interaktiv.IJSRuntime fungerar inte under prerender — vänta till OnAfterRender(firstRender).@* Stäng av prerender om det orsakar problem *@
@rendermode @(new InteractiveServerRenderMode(prerender: false))
Det klassiska mönstret kräver ungefär 20 rader runt din komponent: injicera tjänsten, registrera en callback, läs/skriv via TryTakeFromJson/PersistAsJson och kom ihåg att Dispose.
@inject PersistentComponentState State
@code {
private List<Product>? products;
private PersistingComponentStateSubscription? sub;
protected override async Task OnInitializedAsync()
{
sub = State.RegisterOnPersisting(PersistData);
if (!State.TryTakeFromJson<List<Product>>("products", out var cached))
{
products = await Api.GetAllAsync();
}
else
{
products = cached;
}
}
private Task PersistData()
{
State.PersistAsJson("products", products);
return Task.CompletedTask;
}
public void Dispose() => sub?.Dispose();
}
I .NET 10 ersätter ett enda attribut hela boilerplate-koden ovan. Markera ett fält eller en property med [PersistentState] — ramverket serialiserar värdet under prerender och fyller i det automatiskt när komponenten blir interaktiv:
@inject ProductApi Api
@code {
[PersistentState]
public List<Product>? Products { get; set; }
protected override async Task OnInitializedAsync()
{
Products ??= await Api.GetAllAsync(); // hoppar API om persisted värde fanns
}
}
[PersistentState]-attributet — ersätter PersistentComponentState-mönstret med 1 rad.ReconnectModal-komponent finns med i Blazor Web App-mallen — standardiserad reconnect-UI för Server-läget.blazor.web.js) levereras som static web asset med automatisk fingerprinting — bättre cache-hantering vid deploy.När du renderar en lista kan Blazor inte alltid lista ut vilken befintlig DOM-nod som ska uppdateras när listan ändras. Utan @key gissar den baserat på position — vilket ger felaktiga animationer, felaktigt fokus, och onödiga re-renders.
@* Utan @key — Blazor matchar på position (rad 0, rad 1, …) *@
@foreach (var item in items)
{
<TodoRow Item="item" />
}
@* Med @key — Blazor matchar på Id oavsett om listan sorteras/filtreras *@
@foreach (var item in items)
{
<TodoRow @key="item.Id" Item="item" />
}
@key kan Blazor flytta fel DOM-nod — textfältets fokus och innehåll hamnar på fel rad. Med @key="item.Id" vet Blazor exakt vilken nod som hör till vilket objekt och bevarar tillståndet rätt.
<Virtualize> renderar bara de rader som faktiskt syns i viewporten. En lista med 10 000 produkter skapar med en vanlig @foreach 10 000 DOM-noder vid laddning — med <Virtualize> skapas kanske 20–30 åt gången.
@* Vanlig loop — skapar alla DOM-noder direkt *@
@foreach (var p in products)
{
<ProductRow Product="p" />
}
@* Virtualize — skapar bara synliga rader + lite buffert *@
<Virtualize Items="products" Context="p" OverscanCount="5">
<ProductRow Product="p" />
</Virtualize>
@* Med async-laddning från server (laddar bara aktuell "sida" av data) *@
<Virtualize ItemsProvider="LoadProducts" Context="p">
<ProductRow Product="p" />
</Virtualize>
@code {
private async ValueTask<ItemsProviderResult<Product>> LoadProducts(
ItemsProviderRequest req)
{
var result = await Api.GetPageAsync(req.StartIndex, req.Count);
return new ItemsProviderResult<Product>(result.Items, result.TotalCount);
}
}
Blazor anropar ShouldRender() före varje omrendering. Returnerar du false hoppas diffingen över helt för den komponenten och alla dess barn.
@code {
[Parameter] public Product Product { get; set; } = default!;
private Product? _lastRendered;
protected override bool ShouldRender()
{
// Rendera bara om produkten faktiskt ändrats
if (Product == _lastRendered) return false;
_lastRendered = Product;
return true;
}
}
ShouldRender sparsamt — det är lätt att introducera buggar där UI:t inte uppdateras trots att data ändrats. Profiltyp: komponenter som renderas väldigt ofta men sällan faktiskt ändras (t.ex. statuspaneler, notifikationsikoner).
CascadingValue triggar re-render i alla komponenter nedåt i trädet som lyssnar. Dela upp i flera mindre, eller använd en Scoped-tjänst med event i stället..csproj för att minska bundle-storlek och öka runtime-hastighet:<PropertyGroup>
<!-- Tar bort oanvänd kod -- minskar bundle med ~30 % *@
<PublishTrimmed>true</PublishTrimmed>
<!-- Kompilerar .NET IL direkt till WebAssembly -- snabbare men större build *@
<RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
# Bygg och publicera till en mapp
dotnet publish -c Release -o ./publish
# WASM AOT (snabbare runtime, större filstorlek)
<PropertyGroup>
<RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>
# Logga in
az login
# Skapa resursgrupp och plan
az group create --name min-blazor-rg --location northeurope
az appservice plan create --name min-blazor-plan \
--resource-group min-blazor-rg \
--sku B1 --is-linux
# Skapa webbappen
az webapp create --resource-group min-blazor-rg \
--plan min-blazor-plan \
--name min-blazor-app \
--runtime "DOTNETCORE:8.0"
# Publicera
cd publish
zip -r site.zip .
az webapp deployment source config-zip --resource-group min-blazor-rg \
--name min-blazor-app \
--src site.zip
publish-mappens innehåll till t.ex. C:\inetpub\wwwroot\MinApp.FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -o /app
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /app
COPY --from=build /app .
ENTRYPOINT ["dotnet", "MinApp.dll"]
dotnet publish -c Release)ASPNETCORE_ENVIRONMENT=ProductionLös övningarna självständigt. Det finns inget facit — lärandet sker i processen.
@foreach, sedan med <Virtualize>. Jämför scroll- och laddningsprestanda.
PersistentComponentState så att datan inte hämtas dubbelt. Bekräfta med en breakpoint.