Delen via


ASP.NET vooraf samengestelde kernstatuspersistentie Blazor

Zonder permanente onderdeelstatus gaat de status die tijdens het genereren wordt gebruikt verloren en moet deze opnieuw worden gemaakt wanneer de app volledig is geladen. Als er asynchroon een status wordt gemaakt, kan de gebruikersinterface flikkeren als de vooraf gegenereerde gebruikersinterface wordt vervangen wanneer het onderdeel opnieuw wordt uitgevoerd.

Overweeg de volgende PrerenderedCounter1 tellermodule. Het component stelt een eerste willekeurige tellerwaarde in tijdens het prerenderen in de OnInitialized levenscyclusmethode. Wanneer het onderdeel vervolgens interactief wordt weergegeven, wordt de initiële tellingswaarde vervangen wanneer OnInitialized het een tweede keer wordt uitgevoerd.

PrerenderedCounter1.razor:

@page "/prerendered-counter-1"
@inject ILogger<PrerenderedCounter1> Logger

<PageTitle>Prerendered Counter 1</PageTitle>

<h1>Prerendered Counter 1</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount;

    protected override void OnInitialized()
    {
        currentCount = Random.Shared.Next(100);
        Logger.LogInformation("currentCount set to {Count}", currentCount);
    }

    private void IncrementCount() => currentCount++;
}

Opmerking

Als de app gebruikmaakt van interactieve routering en de pagina wordt bereikt via een interne verbeterde navigatie, gebeurt het prerendering niet. Daarom moet u een volledige pagina opnieuw laden voor het PrerenderedCounter1-onderdeel om de volgende uitvoer te zien. Zie de sectie Interactieve routering en prerendering voor meer informatie.

Voer de app uit en inspecteer logboekregistratie vanuit het onderdeel. Hier volgt voorbeelduitvoer.

info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 41
info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 92

Het eerste geregistreerde aantal wordt vastgesteld tijdens de voorweergave. Het aantal wordt opnieuw ingesteld na het vooraf renderen wanneer het component opnieuw wordt gerenderd. Er is ook een flikkering in de gebruikersinterface wanneer het aantal wordt bijgewerkt van 41 naar 92.

Als u de initiële waarde van de teller tijdens het prerenderen wilt behouden, Blazor ondersteunt het behouden van status op een vooraf gerenderde pagina met behulp van de PersistentComponentState-service (en voor componenten die zijn ingesloten in pagina's of weergaven van Razor Pages of MVC-apps, de Tag Helper voor persistente componentstatus).

Door onderdelen te initialiseren met dezelfde status die tijdens het prerenderen wordt gebruikt, worden dure initialisatiestappen slechts één keer uitgevoerd. De gerenderde gebruikersinterface komt ook overeen met de vooraf gegenereerde gebruikersinterface, dus er treedt geen flikkering op in de browser.

De persistente vooraf gegenereerde status wordt overgebracht naar de client, waar deze wordt gebruikt om de status van het onderdeel te herstellen. Tijdens het renderen aan de clientzijde (CSR, InteractiveWebAssembly), worden de gegevens blootgesteld aan de browser en mogen ze geen gevoelige, persoonlijke gegevens bevatten. Tijdens interactieve rendering aan de serverzijde (interactieve SSR, InteractiveServer), zorgt ASP.NET Core Data Protection ervoor dat de gegevens veilig worden overgedragen. De InteractiveAuto weergavemodus combineert WebAssembly en Server-interactiviteit, dus het is noodzakelijk om gegevensblootstelling in de browser te overwegen, zoals in het CSR-geval.

Om de voorgerenderde status te behouden, gebruikt u het [PersistentState]-kenmerk om de status in eigenschappen te behouden. Eigenschappen met dit kenmerk worden tijdens het prerenderen automatisch opgeslagen met behulp van de PersistentComponentState service. De status wordt opgehaald wanneer het onderdeel interactief wordt weergegeven of wanneer de service wordt geïnstantieerd.

Standaard worden eigenschappen geserialiseerd met behulp van de System.Text.Json serialisatiefunctie met standaardinstellingen en behouden in de vooraf weergegeven HTML. Serialisatie is niet veilig en vereist behoud van de gebruikte typen. Zie De trimmer configureren voor ASP.NET Core Blazorvoor meer informatie.

Het volgende telleronderdeel blijft de tellerstatus behouden tijdens het prerenderen en haalt de status op om het onderdeel te initialiseren:

  • Het [PersistentState] attribuut wordt toegepast op het nullwaarde type (intCurrentCount).
  • De status van de teller wordt toegewezen wanneer null in OnInitialized en automatisch hersteld wanneer het onderdeel interactief wordt gerenderd.

PrerenderedCounter2.razor:

@page "/prerendered-counter-2"
@inject ILogger<PrerenderedCounter2> Logger

<PageTitle>Prerendered Counter 2</PageTitle>

<h1>Prerendered Counter 2</h1>

<p role="status">Current count: @CurrentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    [PersistentState]
    public int? CurrentCount { get; set; }

    protected override void OnInitialized()
    {
        if (CurrentCount is null)
        {
            CurrentCount = Random.Shared.Next(100);
            Logger.LogInformation("CurrentCount set to {Count}", CurrentCount);
        }
        else
        {
            Logger.LogInformation("CurrentCount restored to {Count}", CurrentCount);
        }
    }

    private void IncrementCount() => CurrentCount++;
}

Wanneer het onderdeel wordt uitgevoerd, wordt CurrentCount slechts eenmaal ingesteld tijdens het prerenderen. De waarde wordt hersteld wanneer het onderdeel opnieuw wordt gebruikt. Hier volgt voorbeelduitvoer.

Opmerking

Als de app gebruikmaakt van interactieve routering en de pagina wordt bereikt via een interne verbeterde navigatie, gebeurt het prerendering niet. Daarom moet u een volledige pagina opnieuw laden voor het onderdeel om de volgende uitvoer te zien. Zie de sectie Interactieve routering en prerendering voor meer informatie.

info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount restored to 96

In het volgende voorbeeld wordt de status voor meerdere onderdelen van hetzelfde type geserialiseerd:

  • Eigenschappen die zijn geannoteerd met het [PersistentState] kenmerk, worden geserialiseerd tijdens het prerendering.
  • Het @key richtlijnattribuut wordt gebruikt om de status correct te koppelen aan het onderdeelexemplaar.
  • De Element eigenschap wordt geïnitialiseerd in de OnInitialized levenscyclusmethode om null-verwijzingsonderzonderingen te voorkomen, net zoals null-verwijzingen worden vermeden voor queryparameters en formuliergegevens.

PersistentChild.razor:

<div>
    <p>Current count: @Element.CurrentCount</p>
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</div>

@code {
    [PersistentState]
    public State Element { get; set; }

    protected override void OnInitialized()
    {
        Element ??= new State();
    }

    private void IncrementCount()
    {
        Element.CurrentCount++;
    }

    private class State
    {
        public int CurrentCount { get; set; }
    }
}

Parent.razor:

@page "/parent"

@foreach (var element in elements)
{
    <PersistentChild @key="element.Name" />
}

Status serialiseren voor services

In het volgende voorbeeld wordt de status voor een afhankelijkheidsinjectieservice geserialiseerd:

  • Eigenschappen die zijn geannoteerd met het [PersistentState] kenmerk, worden geserialiseerd tijdens het prerendering en gedeserialiseerd wanneer de app interactief wordt.
  • De RegisterPersistentService extensiemethode wordt gebruikt om de service te registreren voor persistentie. De rendermodus is vereist omdat de weergavemodus niet kan worden afgeleid van het servicetype. Gebruik een van de volgende waarden:
    • RenderMode.Server: De service is beschikbaar voor de weergavemodus Interactive Server.
    • RenderMode.Webassembly: De service is beschikbaar voor de weergavemodus Interactive WebAssembly.
    • RenderMode.InteractiveAuto: De service is beschikbaar voor zowel de weergavemodi Interactieve Server als Interactieve Webassembly als een component in een van die modi wordt weergegeven.
  • De service wordt opgelost tijdens de initialisatie van een interactieve rendermodus en de eigenschappen die zijn geannoteerd met het [PersistentState] kenmerk, worden gedeserialiseerd.

Opmerking

Alleen permanente scoped services worden ondersteund.

Geserialiseerde eigenschappen worden geïdentificeerd vanuit het werkelijke service-exemplaar:

  • Met deze benadering kunt u een abstractie markeren als een permanente service.
  • Hiermee kunnen werkelijke implementaties intern of verschillende typen zijn.
  • Ondersteunt gedeelde code in verschillende assembly's.
  • Resulteert in elk exemplaar dat dezelfde eigenschappen toont.

De volgende tellerservice, CounterTrackermarkeert de huidige tellingseigenschap, CurrentCount met het [PersistentState] kenmerk. De eigenschap wordt geserialiseerd tijdens het prerendering en gedeserialiseerd wanneer de app interactief wordt waar de service wordt geïnjecteerd.

CounterTracker.cs:

public class CounterTracker
{
    [PersistentState]
    public int CurrentCount { get; set; }

    public void IncrementCount()
    {
        CurrentCount++;
    }
}

Registreer in het Program bestand de scoped service en registreer de service voor persistentie bij RegisterPersistentService. In het volgende voorbeeld is de CounterTracker service beschikbaar voor de weergavemodi Interactive Server en Interactive WebAssembly als een onderdeel in een van deze modi wordt weergegeven omdat deze is geregistreerd bij RenderMode.InteractiveAuto.

Als het Program bestand de Microsoft.AspNetCore.Components.Web naamruimte nog niet gebruikt, voegt u de volgende using instructie toe aan het begin van het bestand:

using Microsoft.AspNetCore.Components.Web;

Waar services zijn geregistreerd in het Program bestand:

builder.Services.AddScoped<CounterTracker>();

builder.Services.AddRazorComponents()
    .RegisterPersistentService<CounterTracker>(RenderMode.InteractiveAuto);

Injecteer de CounterTracker service in een onderdeel en gebruik deze om een teller te verhogen. Voor demonstratiedoeleinden in het volgende voorbeeld is de waarde van de eigenschap van de service CurrentCount alleen ingesteld op 10 tijdens het prerenderen.

Pages/Counter.razor:

@page "/counter"
@inject CounterTracker CounterTracker

<PageTitle>Counter</PageTitle>

<h1>Counter</h1>

<p>Rendering: @RendererInfo.Name</p>

<p role="status">Current count: @CounterTracker.CurrentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    protected override void OnInitialized()
    {
        if (!RendererInfo.IsInteractive)
        {
            CounterTracker.CurrentCount = 10;
        }
    }

    private void IncrementCount()
    {
        CounterTracker.IncrementCount();
    }
}

Als u de voorgaande component wilt gebruiken om het aantal 10 in CounterTracker.CurrentCount te behouden, navigeert u naar de component en vernieuwt u de browser, waardoor het prerenderen wordt geactiveerd. Wanneer prerenderen plaatsvindt, ziet u kort "Static", voordat na de uiteindelijke rendering "Server" wordt weergegeven. De teller begint bij 10.

PersistentComponentState De service rechtstreeks gebruiken in plaats van het declaratieve model

Als alternatief voor het gebruik van het declaratieve model voor persistentie met het [PersistentState] kenmerk kunt u de PersistentComponentState service rechtstreeks gebruiken, wat meer flexibiliteit biedt voor complexe scenario's voor statuspersistentie. Roep PersistentComponentState.RegisterOnPersisting aan om een callback te registreren om de status van het onderdeel tijdens het voorrenderen te behouden. De status wordt opgehaald wanneer het onderdeel interactief wordt weergegeven. Maak de aanroep aan het einde van de initialisatiecode om tijdens het afsluiten van de app een potentiële raceconditie te voorkomen.

In het volgende voorbeeld van het tellerelement wordt de tellerstatus behouden tijdens het prerenderen en wordt de status opgehaald om het element te initialiseren.

PrerenderedCounter3.razor:

@page "/prerendered-counter-3"
@implements IDisposable
@inject ILogger<PrerenderedCounter3> Logger
@inject PersistentComponentState ApplicationState

<PageTitle>Prerendered Counter 3</PageTitle>

<h1>Prerendered Counter 3</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount;
    private PersistingComponentStateSubscription persistingSubscription;

    protected override void OnInitialized()
    {
        if (!ApplicationState.TryTakeFromJson<int>(
            nameof(currentCount), out var restoredCount))
        {
            currentCount = Random.Shared.Next(100);
            Logger.LogInformation("currentCount set to {Count}", currentCount);
        }
        else
        {
            currentCount = restoredCount!;
            Logger.LogInformation("currentCount restored to {Count}", currentCount);
        }

        // Call at the end to avoid a potential race condition at app shutdown
        persistingSubscription = ApplicationState.RegisterOnPersisting(PersistCount);
    }

    private Task PersistCount()
    {
        ApplicationState.PersistAsJson(nameof(currentCount), currentCount);

        return Task.CompletedTask;
    }

    private void IncrementCount() => currentCount++;

    void IDisposable.Dispose() => persistingSubscription.Dispose();
}

Wanneer het onderdeel wordt uitgevoerd, wordt currentCount slechts eenmaal ingesteld tijdens het prerenderen. De waarde wordt hersteld wanneer het onderdeel opnieuw wordt gebruikt. Hier volgt voorbeelduitvoer.

Opmerking

Als de app gebruikmaakt van interactieve routering en de pagina wordt bereikt via een interne verbeterde navigatie, gebeurt het prerendering niet. Daarom moet u een volledige pagina opnieuw laden voor het onderdeel om de volgende uitvoer te zien. Zie de sectie Interactieve routering en prerendering voor meer informatie.

info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount restored to 96

Als u de vooraf samengestelde status wilt behouden, bepaalt u welke status moet worden behouden met behulp van de PersistentComponentState-service. PersistentComponentState.RegisterOnPersisting registreert een callback om de status van het onderdeel tijdens het voorbereiden vast te houden. De status wordt opgehaald wanneer het onderdeel interactief wordt weergegeven. Maak de aanroep aan het einde van de initialisatiecode om tijdens het afsluiten van de app een potentiële raceconditie te voorkomen.

In het volgende voorbeeld van het tellerelement wordt de tellerstatus behouden tijdens het prerenderen en wordt de status opgehaald om het element te initialiseren.

PrerenderedCounter2.razor:

@page "/prerendered-counter-2"
@implements IDisposable
@inject ILogger<PrerenderedCounter2> Logger
@inject PersistentComponentState ApplicationState

<PageTitle>Prerendered Counter 2</PageTitle>

<h1>Prerendered Counter 2</h1>

<p role="status">Current count: @currentCount</p>

<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>

@code {
    private int currentCount;
    private PersistingComponentStateSubscription persistingSubscription;

    protected override void OnInitialized()
    {
        if (!ApplicationState.TryTakeFromJson<int>(
            nameof(currentCount), out var restoredCount))
        {
            currentCount = Random.Shared.Next(100);
            Logger.LogInformation("currentCount set to {Count}", currentCount);
        }
        else
        {
            currentCount = restoredCount!;
            Logger.LogInformation("currentCount restored to {Count}", currentCount);
        }

        // Call at the end to avoid a potential race condition at app shutdown
        persistingSubscription = ApplicationState.RegisterOnPersisting(PersistCount);
    }

    private Task PersistCount()
    {
        ApplicationState.PersistAsJson(nameof(currentCount), currentCount);

        return Task.CompletedTask;
    }

    void IDisposable.Dispose() => persistingSubscription.Dispose();

    private void IncrementCount() => currentCount++;
}

Wanneer het onderdeel wordt uitgevoerd, wordt currentCount slechts eenmaal ingesteld tijdens het prerenderen. De waarde wordt hersteld wanneer het onderdeel opnieuw wordt gebruikt. Hier volgt voorbeelduitvoer.

Opmerking

Als de app gebruikmaakt van interactieve routering en de pagina wordt bereikt via een interne verbeterde navigatie, gebeurt het prerendering niet. Daarom moet u een volledige pagina opnieuw laden voor het onderdeel om de volgende uitvoer te zien. Zie de sectie Interactieve routering en prerendering voor meer informatie.

info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount restored to 96

Serialisatie-uitbreidbaarheid voor permanente onderdeelstatus

Implementeer een aangepaste serializer met de IPersistentComponentStateSerializer interface. Zonder een geregistreerde aangepaste serialisatie valt serialisatie terug op de bestaande JSON-serialisatie.

De aangepaste serializer is geregistreerd in het bestand van Program de app. In het volgende voorbeeld is het CustomUserSerializer geregistreerd voor het User type:

builder.Services.AddSingleton<IPersistentComponentStateSerializer<User>, 
    CustomUserSerializer>();

Het type wordt automatisch behouden en hersteld met de aangepaste serializer:

[PersistentState] 
public User? CurrentUser { get; set; } = new();

Onderdelen die zijn ingebed in pagina's en weergaven (Razor Pagina's/MVC)

Voor componenten die zijn ingesloten in een pagina of weergave van een Razor Pages- of MVC-app, moet u de Persist Component State Tag Helper met de <persist-component-state /> HTML-tag binnen de sluitende </body>-tag van de lay-out van de app toevoegen. Dit is alleen vereist voor Razor Pages- en MVC-apps. Zie Helper voor persistente statustags in ASP.NET Core voor meer informatie.

Pages/Shared/_Layout.cshtml:

<body>
    ...

    <persist-component-state />
</body>

Interactieve routing en prerendering

Wanneer het Routes-onderdeel geen weergavemodus definieert, gebruikt de app interactiviteit en navigatie per pagina/onderdeel. Met navigatie per pagina/onderdeel wordt interne navigatie verwerkt door verbeterde routering nadat de app interactief is. Interne navigatie in deze context betekent dat de URL-bestemming van de navigatie-gebeurtenis een Blazor eindpunt in de app is.

Blazor ondersteunt het verwerken van de status van permanente onderdelen tijdens verbeterde navigatie. Status behouden tijdens verbeterde navigatie kan worden gelezen door interactieve onderdelen op de pagina.

De status van permanente onderdelen wordt standaard alleen geladen door interactieve onderdelen wanneer deze in eerste instantie op de pagina worden geladen. Dit voorkomt dat belangrijke status, zoals gegevens in een bewerkt webformulier, wordt overschreven als er extra uitgebreide navigatiegebeurtenissen naar dezelfde pagina plaatsvinden nadat het onderdeel is geladen.

Als de gegevens alleen-lezen zijn en niet regelmatig worden gewijzigd, meldt u zich aan om updates toe te staan tijdens verbeterde navigatie door het AllowUpdates = true in te stellen[PersistentState]. Dit is handig voor scenario's zoals het weergeven van gegevens in de cache die duur zijn om op te halen, maar die niet vaak veranderen. In het volgende voorbeeld ziet u het gebruik van AllowUpdates weersvoorspellingsgegevens:

[PersistentState(AllowUpdates = true)]
public WeatherForecast[]? Forecasts { get; set; }

protected override async Task OnInitializedAsync()
{
    Forecasts ??= await ForecastService.GetForecastAsync();
}

Als u de herstelstatus tijdens het voorbereiden wilt overslaan, stelt u het volgende in RestoreBehaviorSkipInitialValue:

[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)]
public string NoPrerenderedData { get; set; }

Als u de herstelstatus tijdens het opnieuw verbinden wilt overslaan, stelt u deze in RestoreBehavior op SkipLastSnapshot. Dit kan handig zijn om ervoor te zorgen dat nieuwe gegevens na opnieuw verbinding worden gemaakt:

[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)]
public int CounterNotRestoredOnReconnect { get; set; }

Aanroep PersistentComponentState.RegisterOnRestoring om een callback te registreren voor imperatief bepalen hoe de status wordt hersteld, vergelijkbaar met hoe PersistentComponentState.RegisterOnPersisting de status volledig wordt gecontroleerd.

De PersistentComponentState-service werkt alleen bij het laden van de eerste pagina en niet bij interne verbeterde paginanavigatie-gebeurtenissen.

Als de app een volledige (niet-uitgebreide) navigatie naar een pagina uitvoert die gebruikmaakt van de status van een permanent onderdeel, wordt de persistente status beschikbaar gesteld voor de app die kan worden gebruikt wanneer deze interactief wordt.

Als er al een interactief circuit tot stand is gebracht en er een verbeterde navigatie wordt uitgevoerd naar een pagina die gebruikmaakt van de permanente onderdeelstatus, wordt de status niet beschikbaar gesteld in het bestaande circuit dat het onderdeel kan gebruiken. Er is geen prerendering voor het interne paginaverzoek en de PersistentComponentState-service is zich er niet van bewust dat er een verbeterde navigatie heeft plaatsgevonden. Er is geen mechanisme om statusupdates te leveren aan onderdelen die al worden uitgevoerd op een bestaand circuit. De reden hiervoor is dat Blazor alleen ondersteuning biedt voor het doorgeven van de status van de server aan de client op het moment dat de runtime wordt geïnitialiseerd, niet nadat de runtime is gestart.

Verbeterde navigatie uitschakelen, waardoor de prestaties worden verminderd, maar ook het probleem van de laadstatus PersistentComponentState voor interne paginaaanvragen wordt vermeden, wordt behandeld in ASP.NET Kernroutering Blazor en -navigatie. U kunt de app ook bijwerken naar .NET 10 of hoger, waarbij Blazor ondersteuning wordt geboden voor het verwerken van de status van permanente onderdelen tijdens verbeterde navigatie.