ASP.NET Core Blazor vorgerenderte Zustandspersistenz

In diesem Artikel wird erläutert, wie Sie den Komponentenstatus in Blazor-Apps mithilfe des Diensts für persistenten Komponentenstatus über das Prerendering hinweg beibehalten. Sie erfahren, wie Sie das [PersistentState] Attribut, den PersistentComponentState Dienst direkt verwenden und benutzerdefinierte Serialisierer für den beständigen Zustand erstellen.

Ohne beibehaltenen Komponentenstatus geht der während der Vorrenderung verwendete Zustand verloren und muss neu erstellt werden, wenn die App vollständig geladen wird. Wenn ein Zustand asynchron erstellt wird, flackert die Benutzeroberfläche möglicherweise, da die zuvor gerenderte Benutzeroberfläche durch temporäre Ladeinhalte ersetzt und dann erneut vollständig gerendert wird.

Betrachten Sie die folgende PrerenderedCounter1-Zählerkomponente. Die Komponente setzt einen anfänglichen zufälligen Zählerwert während des Vorrenderings in der OnInitialized-Lebenszyklusmethode. Wenn die Komponente dann interaktiv gerendert wird, wird der anfängliche Zählwert ersetzt, wenn OnInitialized ein zweites Mal ausgeführt wird.

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

Hinweis

Wenn die App interaktives Routing annimmt und die Seite über eine interne erweiterte Navigation erreicht wird, tritt die Voraberstellung nicht auf. Daher müssen Sie die gesamte Seite für die PrerenderedCounter1-Komponente neu laden, um die folgende Ausgabe anzuzeigen. Weitere Informationen finden Sie im Abschnitt "Interaktives Routing und Vorschreiben" .

Führen Sie die App aus, und prüfen Sie die Protokollierung aus der Komponente. Es folgt eine Beispielausgabe.

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

Die erste protokollierte Anzahl erfolgt während des Vorabrenderings. Die Anzahl wird erneut festgelegt, nachdem die Komponente neu gerendert wurde. Die Benutzeroberfläche flackert auch, wenn die Anzahl von 41 auf 92 aktualisiert wird.

Um den Anfangswert des Zählers während der Vorrenderung beizubehalten, unterstützt Blazor das Beibehalten des Zustands auf einer vorgerenderten Seite mithilfe des PersistentComponentState Dienstes (und für Komponenten, die in Seiten oder Ansichten von Razor Seiten oder MVC-Apps eingebettet sind, den Persist Component State Tag Helper).

Durch das Initialisieren von Komponenten mit demselben Zustand, der während des Prerenderings verwendet wird, werden teure Initialisierungsschritte nur einmal ausgeführt. Die gerenderte Benutzeroberfläche entspricht auch der vorab gerenderten Benutzeroberfläche, sodass im Browser kein Flimmern auftritt.

Der beibehaltene vorrenderte Zustand wird an den Client übertragen, in dem er zum Wiederherstellen des Komponentenzustands verwendet wird. Während des clientseitigen Renderings (CSR) InteractiveWebAssemblywerden die Daten für den Browser verfügbar gemacht und dürfen keine vertraulichen, privaten Informationen enthalten. Während des interaktiven serverseitigen Renderings (interaktiver SSR, InteractiveServer), stellt ASP.NET Core Datenschutz sicher, dass die Daten sicher übertragen werden. Der InteractiveAuto Rendermodus kombiniert WebAssembly und Server-Interaktivität, daher ist es notwendig, die Datenexposition für den Browser zu berücksichtigen, wie im CSR-Fall.

Um den voreingestellten Zustand mithilfe des PersistentComponentState Diensts beizubehalten, wenden Sie das [PersistentState] Attribut auf Eigenschaften an public . Der Zustand wird abgerufen, wenn die Komponente interaktiv gerendert wird oder der PersistentComponentState Dienst instanziiert wird.

Verwenden Sie public Eigenschaften, da Spiegelung vom Framework für Aufgaben wie das Kürzen nicht verwendeter Code und die Quellgenerierung verwendet wird.

Standardmäßig werden Eigenschaften mithilfe des System.Text.Json Serialisierungsprogramms mit Standardeinstellungen serialisiert und im vordefinierten HTML-Code beibehalten. Die Serialisierung ist nicht trimmersicher und erfordert die Beibehaltung der verwendeten Typen. Weitere Informationen finden Sie unter Configure the Trimmer for ASP.NET Core Blazor.

Die folgende Zählerkomponente behält den Zählerzustand während des Vorrenderns bei und stellt den Zustand wieder her, um die Komponente zu initialisieren.

  • Das [PersistentState] Attribut wird auf die öffentliche nullable CurrentCount Eigenschaft des Typs int?angewendet.
  • Der Status des Zählers wird zugewiesen, wenn null in OnInitialized und automatisch wiederhergestellt, wenn die Komponente interaktiv gerendert wird.

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

Wenn die Komponente ausgeführt wird, wird CurrentCount während des Vorabrenderns nur einmal gesetzt. Der Wert wird wiederhergestellt, wenn die Komponente erneut gerendert wird. Es folgt eine Beispielausgabe.

Hinweis

Wenn die App interaktives Routing annimmt und die Seite über eine interne erweiterte Navigation erreicht wird, tritt die Voraberstellung nicht auf. Daher müssen Sie eine vollständige Seite neu laden, damit die Komponente die folgende Ausgabe sehen kann. Weitere Informationen finden Sie im Abschnitt "Interaktives Routing und Vorschreiben" .

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

Im folgenden Beispiel, das den Zustand für mehrere Komponenten desselben Typs serialisiert:

  • Öffentliche Eigenschaften, die mit dem Attribut versehen sind, werden während des Vorab-Renderings serialisiert.
  • Das @key Direktive-Attribut wird verwendet, um sicherzustellen, dass der Zustand ordnungsgemäß der Komponenteninstanz zugeordnet ist.
  • Die Element Eigenschaft wird in der OnInitialized Lebenszyklusmethode initialisiert, um Nullverweisausnahmen zu vermeiden, ähnlich wie NULL-Verweise für Abfrageparameter und Formulardaten vermieden werden.

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" />
}

Hinweis

Eine benutzerkonfigurierte JsonSerializerContext wird nicht an die C#-Union-Deserialisierung weitergegeben. Verwenden Sie stattdessen [JsonUnion(TypeClassifier = typeof({TYPE CLASSIFIER}))], wobei {TYPE CLASSIFIER} der Platzhalter für den Typklassifikator ist.

Serialisieren des Zustands für Dienste

Im folgenden Beispiel, das den Zustand für einen Abhängigkeitsinjektionsdienst serialisiert:

  • Eigenschaften, die mit dem [PersistentState] Attribut annotiert sind, werden während des Pre-Renderings serialisiert und deserialisiert, wenn die App interaktiv ist.
  • Die RegisterPersistentService Erweiterungsmethode wird verwendet, um den Dienst für Persistenz zu registrieren. Der Rendermodus ist erforderlich, da der Rendermodus nicht vom Diensttyp abgeleitet werden kann. Verwenden Sie einen der folgenden Werte:
    • RenderMode.Server: Der Dienst ist für den Interaktiven Server-Rendermodus verfügbar.
    • RenderMode.Webassembly: Der Dienst ist für den interaktiven Webassembly-Rendermodus verfügbar.
    • RenderMode.InteractiveAuto: Der Dienst ist sowohl für den Interaktiven Server als auch für den interaktiven Webassembmbly-Rendermodus verfügbar, wenn eine Komponente in einem dieser Modi gerendert wird.
  • Der Dienst wird während der Initialisierung eines interaktiven Rendermodus aufgelöst, und die mit dem [PersistentState] Attribut versehenen Eigenschaften werden deserialisiert.

Hinweis

Auf dem server wird nur das Persistieren von Diensten mit Bereichsbindung unterstützt. Auf dem WebAssembly-Client (InteractiveAuto oder InteractiveWebAssembly Rendermodi) muss der Dienst als Singleton registriert werden. Weitere Informationen finden Sie unter Registrierung des Clientprojektservice.

Serialisierte Eigenschaften werden aus der tatsächlichen Dienstinstanz identifiziert:

  • Dieser Ansatz ermöglicht das Markieren einer Abstraktion als beständigen Dienst.
  • Ermöglicht es, dass tatsächliche Implementierungen entweder intern oder von unterschiedlichen Typen sind.
  • Unterstützt gemeinsam genutzten Code in verschiedenen Assemblys.
  • Dies führt dazu, dass jede Instanz die gleichen Eigenschaften aufweist.

Der folgende Zählerservice CounterTracker markiert seine aktuelle Zähl-Eigenschaft CurrentCount mit dem [PersistentState] Attribut. Die öffentliche Eigenschaft wird beim Prerendering serialisiert und deserialisiert, wenn die App interaktiv wird, wo auch immer der Dienst injiziert wird.

CounterTracker.cs:

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

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

Registrierung des Serverprojektdiensts

Registrieren Sie in der Program-Datei des Serverprojekts den bereichsbezogenen Dienst und registrieren Sie den Dienst für Persistenz bei RegisterPersistentService. Im folgenden Beispiel ist der CounterTracker Dienst sowohl für den interaktiven Server- als auch für den interaktiven WebAssembly-Rendermodus verfügbar, wenn eine Komponente in einem dieser Modi gerendert wird, da sie mit RenderMode.InteractiveAuto registriert ist.

Wenn die Datei Program den Namespace Microsoft.AspNetCore.Components.Web nicht bereits verwendet, fügen Sie die folgende using-Anweisung am Anfang der Datei hinzu:

using Microsoft.AspNetCore.Components.Web;

Wo Dienste in der Datei des Serverprojekts Program registriert sind:

builder.Services.AddScoped<CounterTracker>();

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

Clientprojektdienstregistrierung

Für RenderMode.InteractiveAuto oder RenderMode.InteractiveWebAssembly muss der Dienst auch in der Datei Program des Projekts .Client registriert werden. Registrieren Sie ihn als Singleton, nicht begrenzt:

builder.Services.AddSingleton<CounterTracker>();

Die clientseitige ComponentStatePersistenceManager Datei wird als Singleton registriert, sodass die Abhängigkeiten vom Stammdienstanbieter aufgelöst werden. Vermeiden Sie eine bereichsbezogene Dienstregistrierung. Wenn der Dienst fälschlicherweise als begrenzt auf dem Client registriert ist, löst das Auflösen des Diensts vom Stammanbieter eine DirectScopedResolvedFromRootException in Entwicklungsumgebungen aus. Über die Ausnahme hinaus erzeugt eine begrenzte Registrierung zwei separate Instanzen auf dem Client: eine, die während der Zustandswiederherstellung aus dem Stamm aufgelöst wurde, und eine andere aus dem App-Bereich für eingefügte Komponenten. Dies bedeutet, dass der wiederhergestellte Zustand nicht die Instanz erreicht, die die Komponenten verwenden. Durch die Registrierung des Diensts als Singleton auf dem Client wird sichergestellt, dass dieselbe Instanz sowohl für die Zustandswiederherstellung als auch für die Komponenteneinfügung verwendet wird.

Fügen Sie den CounterTracker Dienst in eine Komponente ein, und verwenden Sie ihn, um einen Zähler zu erhöhen. Für Demonstrationszwecke im folgenden Beispiel wird der Wert der Eigenschaft des Diensts CurrentCount nur während der Vorrenderung auf 10 festgelegt.

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

Um die vorherige Komponente zu nutzen, um das Beibehalten der Zählung von 10 in CounterTracker.CurrentCount zu demonstrieren, navigieren Sie zur Komponente und aktualisieren Sie den Browser, was das Prerendering auslöst. Beim Prerendering zeigt RendererInfo.Name kurz „Static“ an, bevor nach dem endgültigen Rendering „Server“ angezeigt wird. Der Zähler beginnt bei 10.

Verwenden des PersistentComponentState Diensts direkt anstelle des deklarativen Modells

Als Alternative zur Verwendung des deklarativen Modells zum Beibehalten des Zustands mit dem [PersistentState] Attribut können Sie den PersistentComponentState Dienst direkt verwenden, der eine größere Flexibilität für komplexe Zustandspersistenzszenarien bietet. Rufen Sie PersistentComponentState.RegisterOnPersisting auf, um einen Callback zu registrieren, um den Komponentenstatus während des Prerenderings aufrechtzuerhalten. Der Zustand wird abgerufen, wenn die Komponente interaktiv gerendert wird. Führen Sie den Aufruf am Ende des Initialisierungscodes aus, um eine potenzielle Racebedingung beim Herunterfahren der App zu vermeiden.

Im folgenden Beispiel für Zählerkomponenten wird der Zählerzustand während des Vorabrenderns beibehalten und der Zustand abgerufen, um die Komponente zu initialisieren.

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

Wenn die Komponente ausgeführt wird, wird currentCount während des Vorabrenderns nur einmal gesetzt. Der Wert wird wiederhergestellt, wenn die Komponente erneut gerendert wird. Es folgt eine Beispielausgabe.

Hinweis

Wenn die App interaktives Routing annimmt und die Seite über eine interne erweiterte Navigation erreicht wird, tritt die Voraberstellung nicht auf. Daher müssen Sie eine vollständige Seite neu laden, damit die Komponente die folgende Ausgabe sehen kann. Weitere Informationen finden Sie im Abschnitt "Interaktives Routing und Vorschreiben" .

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

Entscheiden Sie, welcher Zustand mithilfe des PersistentComponentState-Diensts beibehalten werden soll, um den vorab gerenderten Zustand beizubehalten. PersistentComponentState.RegisterOnPersisting registriert einen Callback, um den Komponentenzustand während des Vorrenderings beizubehalten. Der Zustand wird abgerufen, wenn die Komponente interaktiv gerendert wird. Führen Sie den Aufruf am Ende des Initialisierungscodes aus, um eine potenzielle Racebedingung beim Herunterfahren der App zu vermeiden.

Im folgenden Beispiel für Zählerkomponenten wird der Zählerzustand während des Vorabrenderns beibehalten und der Zustand abgerufen, um die Komponente zu initialisieren.

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

Wenn die Komponente ausgeführt wird, wird currentCount während des Vorabrenderns nur einmal gesetzt. Der Wert wird wiederhergestellt, wenn die Komponente erneut gerendert wird. Es folgt eine Beispielausgabe.

Hinweis

Wenn die App interaktives Routing annimmt und die Seite über eine interne erweiterte Navigation erreicht wird, tritt die Voraberstellung nicht auf. Daher müssen Sie eine vollständige Seite neu laden, damit die Komponente die folgende Ausgabe sehen kann. Weitere Informationen finden Sie im Abschnitt "Interaktives Routing und Vorschreiben" .

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

Serialisierungserweiterung für beständigen Komponentenzustand

Implementieren Sie einen benutzerdefinierten Serialisierer mit PersistentComponentStateSerializer<T>. Ohne einen registrierten benutzerdefinierten Serialisierer greift die Serialisierung auf die vorhandene JSON-Serialisierung zurück.

Im folgenden Beispiel wird ein benutzerdefinierter Serializer für die Typen int? erstellt, der ein benutzerdefiniertes Format verwendet, um die Erweiterbarkeit der Serialisierung zu veranschaulichen. Der Serialisierer präfixiert ganzzahlige Werte mit "CUSTOM:", um sie deutlich von der JSON-Serialisierung zu unterscheiden.

Der Serialisierer arbeitet mit nullablen und nicht nullablen Typen unabhängig voneinander, daher wird der folgende Serialisierer nicht auf int-Typen angewendet; verarbeitet werden nur nullable Integer-Typen (int?).

Die Anmeldung im folgenden Beispiel dient zu Demonstrationszwecken und wird normalerweise nicht in einer Produktions-App implementiert.

CustomIntSerializer.cs:

using System;
using System.Buffers;
using System.Text;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.Logging;

namespace BlazorSample;

public class CustomIntSerializer(ILogger<CustomIntSerializer> logger) 
    : PersistentComponentStateSerializer<int?>
{
    public override void Persist(int? value, IBufferWriter<byte> writer)
    {
        string customFormat = 
            value is not null ? $"CUSTOM:{value}" : $"CUSTOM:null";

        logger.LogInformation(
            "Persisting value {Value} with custom format: {CustomFormat}", value, 
            customFormat);

        byte[] bytes = Encoding.UTF8.GetBytes(customFormat);
        writer.Write(bytes);
    }

    public override int? Restore(ReadOnlySequence<byte> data)
    {
        byte[] bytes = data.ToArray();
        string text = Encoding.UTF8.GetString(bytes);

        logger.LogInformation("Restoring value from custom format: {CustomFormat}", 
            text);

        if (text.StartsWith("CUSTOM:", StringComparison.Ordinal))
        {
            var remainingText = text.AsSpan(7);

            if (!remainingText.SequenceEqual("null"))
            {
                if (int.TryParse(remainingText, out int value))
                {
                    return value;
                }
            }
            else
            {
                return null;
            }
        }

        // Fallback to direct parsing if format is unexpected
        return int.TryParse(text, out int fallbackValue) ? fallbackValue : null;
    }
}

Der benutzerdefinierte Serialisierer wird in der Datei der App Program registriert:

builder.Services.AddSingleton<PersistentComponentStateSerializer<int?>, 
    CustomIntSerializer>();

Der int? Typ wird automatisch beibehalten und mit dem benutzerdefinierten Serialisierer wiederhergestellt:

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

Bei Verwendung des zuvor gezeigten Serializers mit der in der PrerenderedCounter2 dieses Artikels dargestellten PrerenderedCounter2.razor-Komponente () wird eine Ausgabe ähnlich der folgenden protokolliert.

Hinweis

Wenn die App interaktives Routing annimmt und die Seite über eine interne erweiterte Navigation erreicht wird, tritt die Voraberstellung nicht auf. Daher müssen Sie eine vollständige Seite neu laden, damit die Komponente die folgende Ausgabe sehen kann. Weitere Informationen finden Sie im Abschnitt "Interaktives Routing und Vorschreiben" .

info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount set to 49
info: BlazorSample.CustomIntSerializer[0]
Persisting value 49 with custom format: CUSTOM:49
info: BlazorSample.CustomIntSerializer[0]
Restoring value from custom format: CUSTOM:49
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount restored to 49

In Seiten und Ansichten eingebettete Komponenten (Razor Pages/MVC)

Für Komponenten, die in eine Seite oder Ansicht von Razor Pages- oder einer MVC-App eingebettet sind, müssen Sie das Hilfsprogramm Persist Component State Tag Helper mit dem <persist-component-state /> HTML-Tag innerhalb des schließenden </body> Tags des App-Layouts hinzufügen. Dies ist nur für Razor Pages- und MVC-Apps erforderlich. Weitere Informationen finden Sie unter Persist Component State Tag Helper in ASP.NET Core.

Pages/Shared/_Layout.cshtml:

<body>
    ...

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

Interaktives Routing und Vorabrendering

Wenn die Routes Komponente keinen Rendermodus definiert, verwendet die App interaktivität und Navigation pro Seite/Komponente. Mithilfe der Navigation pro Seite/Komponente wird die interne Navigation durch erweitertes Routing gesteuert, nachdem die App interaktiv wird. "Interne Navigation" in diesem Kontext bedeutet, dass das URL-Ziel des Navigationsereignisses ein Blazor Endpunkt innerhalb der App ist.

Blazor unterstützt die Handhabung des zustandsbeständigen Zustands von Komponenten während der erweiterten Navigation. Der während der erweiterten Navigation persistierte Status kann von interaktiven Komponenten auf der Seite gelesen werden.

Standardmäßig wird der persistierte Komponentenstatus nur von interaktiven Komponenten geladen, wenn diese auf der Seite zum ersten Mal geladen werden. Dadurch wird verhindert, dass ein wichtiger Zustand, z. B. Daten in einem bearbeiteten Webformular, überschrieben wird, wenn nach dem Laden der Komponente zusätzliche erweiterte Navigationsereignisse zu derselben Seite auftreten.

Wenn die Daten schreibgeschützt sind und sich nicht häufig ändern, können Sie Aktualisierungen bei der erweiterten Navigation zulassen, indem Sie AllowUpdates auf true für das [PersistentState]-Attribut festlegen. Dies ist nützlich für Szenarien wie das Anzeigen zwischengespeicherter Daten, die teuer zum Abrufen sind, sich aber nicht häufig ändern, z. B. Wettervorhersagedaten im folgenden Beispiel:

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

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

Hinweis

PersistentStateAttribute.AllowUpdates steuert nicht, ob der aktuelle Eigenschaftswert durch Persistenz des Schaltkreiszustands erfasst wird, wenn der Schaltkreis pausiert, einschließlich einer automatischen Pause aufgrund von Inaktivität des Tabs.

Hinweis

PersistentStateAttribute.AllowUpdates steuert nicht, ob der aktuelle Eigenschaftswert durch Schaltungszustandspersistenz erfasst wird, wenn der Schaltkreis angehalten wird.

Um den Wiederherstellungszustand während der Vorrenderung zu überspringen, setzen Sie RestoreBehavior auf SkipInitialValue.

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

Um die Wiederherstellung des Status während der erneuten Verbindung zu überspringen, setzen Sie RestoreBehavior auf SkipLastSnapshot. Dies kann hilfreich sein, um neue Daten nach der erneuten Verbindung sicherzustellen:

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

Rufen Sie PersistentComponentState.RegisterOnRestoring auf, um einen Callback zu registrieren und imperativ zu steuern, wie der Zustand wiederhergestellt wird, ähnlich wie PersistentComponentState.RegisterOnPersisting vollständige Kontrolle über das Beibehalten des Zustands bietet.

Der PersistentComponentState-Dienst funktioniert nur beim Laden der ersten Seite und nicht bei internen erweiterten Seitennavigationsereignissen.

Wenn die App eine vollständige (nicht erweiterte) Navigation zu einer Seite ausführt, die den permanenten Komponentenstatus verwendet, wird der permanente Status für die App verfügbar gemacht, wenn sie interaktiv wird.

Wenn bereits ein interaktiver Schaltkreis eingerichtet wurde und eine erweiterte Navigation auf einer Seite ausgeführt wird, die den permanenten Komponentenzustand verwendet, wird der Zustand nicht im vorhandenen Schaltkreis für die zu verwendende Komponente verfügbar gemacht. Es gibt kein Vorabrendern für die interne Seitenanfrage, und der PersistentComponentState-Dienst ist sich nicht bewusst, dass eine erweiterte Navigation stattgefunden hat. Es gibt keinen Mechanismus zum Bereitstellen von Zustandsupdates für Komponenten, die bereits auf einer vorhandenen Schaltung ausgeführt werden. Der Grund dafür ist, dass Blazor nur das Übergeben des Zustands vom Server an den Client unterstützt, wenn die Laufzeit initialisiert wird, nicht nach dem Start der Laufzeit.

Das Deaktivieren der erweiterten Navigation, wodurch die Leistung verringert wird, aber auch das Problem beim Laden des Ladezustands mit PersistentComponentState für interne Seitenanforderungen vermieden wird, wird in ASP.NET Core Blazor Navigation behandelt. Alternativ können Sie die Anwendung auf .NET 10 oder höher aktualisieren, wo Blazor den Umgang mit persistenten Komponentenzuständen bei der erweiterten Navigation unterstützt.