Del 8 av 8

Render modes, prestanda & deployment

Alla render modes på djupet, prerendering, prestandaverktyg och hur du publicerar appen till Azure App Service eller IIS.

Lektion 8 — V8. Vi kopplar ihop kursen: när väljer man Server, WASM eller Auto? Hur fungerar prerendering? Och hur publicerar vi appen så att riktiga användare når den?

Render modes — fördjupning

ModeFörsta laddningInteraktivitetKrav på server
Static SSRMycket snabb (ren HTML)Ingen (formulär funkar via POST)Bara HTTP-svar
ServerSnabb (liten HTML + JS)Omedelbar, kräver SignalRKonstant WebSocket-uppkoppling
WebAssemblyLångsam (laddar .NET-runtime)Omedelbar efter laddningStatisk filserver räcker
AutoSnabb som ServerOmedelbar, byter till WASMServer + statiska filer

Sätta render mode per komponent

@page "/counter"
@rendermode InteractiveServer

<h1>Räknare</h1>
<button @onclick="() => count++">@count</button>

@code { private int count; }

Tillgängliga värden:

Du kan också sätta render mode när du använder en komponent:

<Counter @rendermode="InteractiveWebAssembly" />

Prerendering

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:

Prerender-fällor
@* Stäng av prerender om det orsakar problem *@
@rendermode @(new InteractiveServerRenderMode(prerender: false))

PersistentComponentState — bevara data över prerender

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();
}

.NET 10: [PersistentState] — deklarativ persistens

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
    }
}
.NET 10-nyheter för render modes & deployment

Prestandatips

1. @key — hjälp Blazor matcha rätt DOM-nod

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" />
}
Konkret problem utan @key Du har en lista med tre rader. Rad 1 har ett öppet textfält med osparad text. Användaren klickar "sortera A–Ö". Utan @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.

2. Virtualize — rendera bara det synliga

<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);
    }
}

3. ShouldRender — hoppa över onödiga re-renders

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;
    }
}
Tänk på Använd 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).

4. Övriga tips

<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>

Bygga för produktion

# Bygg och publicera till en mapp
dotnet publish -c Release -o ./publish

# WASM AOT (snabbare runtime, större filstorlek)
<PropertyGroup>
    <RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>

Deployment — Azure App Service

# 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

Deployment — IIS (Windows-server)

  1. Installera ASP.NET Core Hosting Bundle på servern.
  2. Kopiera publish-mappens innehåll till t.ex. C:\inetpub\wwwroot\MinApp.
  3. Skapa en ny site eller application i IIS Manager som pekar på mappen.
  4. Sätt Application Pool till .NET CLR Version: No Managed Code.
  5. Konfigurera HTTPS-bindning.

Docker

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"]

Checklista innan deploy

Glöm inte

Referenser

Elektroniska resurser

Böcker

Övningar

Lös övningarna självständigt. Det finns inget facit — lärandet sker i processen.

  1. Render mode-experiment Bygg en sida med fyra varianter av samma räknare: Static, Server, WASM och Auto. Notera nätverkstrafik och första-laddningstid för var och en.
  2. Virtualize en lång lista Generera 10 000 rader data och visa dem först med en vanlig @foreach, sedan med <Virtualize>. Jämför scroll- och laddningsprestanda.
  3. Persistent state över prerender Bygg en komponent som hämtar data från ett API. Använd PersistentComponentState så att datan inte hämtas dubbelt. Bekräfta med en breakpoint.
  4. Publicera till Azure (eller IIS) Skapa en gratis Azure-prenumeration (eller använd en lokal IIS-server) och publicera ditt projekt. Verifiera att HTTPS fungerar och att appen svarar publikt.

Soloprojektor

Projekt 1 — Kursportföljen i produktion Ta din kursportfölj från L1 och publicera den till Azure App Service eller IIS. Lägg till en sida som beskriver vilket render mode du valde och varför.
Projekt 2 — Hybridapp (fördjupning) Bygg en app där landningssidan är Static SSR (för SEO), produktlistan är Server (för snabb start) och en redigerare är WASM (för offline-arbete). Motivera ditt val per sida i README.

← Föregående: API & Auth Nästa: Examination →