Blazor zarządzanie stanem podstawowego ASP.NET

Uwaga

Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

Ważne

Te informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany, zanim zostanie wydany komercyjnie. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.

Aby zapoznać się z bieżącą wersją, zapoznaj się z wersją tego artykułu platformy .NET 8.

W tym artykule opisano typowe podejścia do obsługi danych (stanu) użytkownika podczas korzystania z aplikacji i sesji przeglądarki.

Uwaga

Przykłady kodu w tym artykule przyjmują typy odwołań dopuszczających wartość null (NRTs) i statyczną analizę stanu null kompilatora platformy .NET, które są obsługiwane w programie ASP.NET Core na platformie .NET 6 lub nowszym. W przypadku określania wartości docelowej ASP.NET Core 5.0 lub starszej usuń oznaczenie typu null (?) z typów w przykładach artykułu.

Obsługa stanu użytkownika

Po stronie Blazor serwera jest stanowa struktura aplikacji. W większości przypadków aplikacja utrzymuje połączenie z serwerem. Stan użytkownika jest przechowywany w pamięci serwera w obwodzie.

Przykłady stanu użytkownika przechowywanego w obwodzie obejmują:

  • Hierarchia wystąpień składników i ich najnowszych danych wyjściowych renderowania w renderowanych interfejsach użytkownika.
  • Wartości pól i właściwości w wystąpieniach składników.
  • Dane przechowywane w wystąpieniach usługi wstrzykiwania zależności (DI), które są ograniczone do obwodu.

Stan użytkownika można również znaleźć w zmiennych JavaScript w pamięci przeglądarki ustawionej za pośrednictwem wywołań międzyoperacyjnych języka JavaScript.

Jeśli użytkownik doświadcza tymczasowej utraty połączenia sieciowego, Blazor próbuje ponownie połączyć użytkownika z oryginalnym obwodem ze stanem oryginalnym. Jednak ponowne połączenie użytkownika z oryginalnym obwodem w pamięci serwera nie zawsze jest możliwe:

  • Serwer nie może zachować odłączonego obwodu na zawsze. Serwer musi zwolnić odłączony obwód po przekroczeniu limitu czasu lub gdy serwer jest pod ciśnieniem pamięci.
  • W środowiskach wdrażania z wieloma serwerami z równoważeniem obciążenia poszczególne serwery mogą zakończyć się niepowodzeniem lub zostać automatycznie usunięte, gdy nie będą już wymagane do obsługi ogólnej liczby żądań. Oryginalny serwer przetwarza żądania dla użytkownika może stać się niedostępny, gdy użytkownik spróbuje ponownie nawiązać połączenie.
  • Użytkownik może zamknąć i ponownie otworzyć przeglądarkę lub ponownie załadować stronę, co spowoduje usunięcie dowolnego stanu przechowywanego w pamięci przeglądarki. Na przykład wartości zmiennych języka JavaScript ustawiane za pomocą wywołań międzyoperacyjnych języka JavaScript są tracone.

Gdy użytkownik nie może być ponownie połączony z oryginalnym obwodem, użytkownik otrzymuje nowy obwód z pustym stanem. Jest to odpowiednik zamykania i ponownego otwierania aplikacji klasycznej.

Stan utrwalania między obwodami

Ogólnie rzecz biorąc, należy zachować stan między obwodami, w których użytkownicy aktywnie tworzą dane, a nie tylko odczytywanie danych, które już istnieją.

Aby zachować stan między obwodami, aplikacja musi utrwalać dane w innej lokalizacji magazynu niż pamięć serwera. Trwałość stanu nie jest automatyczna. Należy wykonać kroki podczas tworzenia aplikacji w celu zaimplementowania stanowej trwałości danych.

Trwałość danych jest zwykle wymagana tylko w przypadku stanu o wysokiej wartości, który użytkownicy wydali nakład pracy na utworzenie. W poniższych przykładach stan utrwalania pozwala zaoszczędzić czas lub pomoc w działaniach komercyjnych:

  • Formularze wieloetapowe sieci Web: czasochłonne jest ponowne wprowadzanie danych przez użytkownika w celu wykonania kilku wykonanych kroków wieloetapowego formularza internetowego, jeśli ich stan zostanie utracony. Użytkownik utraci stan w tym scenariuszu, jeśli odejdzie od formularza i wróci później.
  • Koszyki na zakupy: każdy składnik aplikacji, który reprezentuje potencjalny przychód, może być utrzymywany. Użytkownik, który traci swój stan, a tym samym koszyk zakupów, może zakupić mniej produktów lub usług po powrocie do witryny później.

Aplikacja może utrwalać tylko stan aplikacji. Interfejsy użytkownika nie mogą być utrwalane, takie jak wystąpienia składników i ich drzewa renderowania. Składniki i drzewa renderowania nie są zwykle serializowalne. Aby utrwalyć stan interfejsu użytkownika, taki jak rozwinięte węzły kontrolki widoku drzewa, aplikacja musi używać niestandardowego kodu do modelowania zachowania stanu interfejsu użytkownika jako stanu aplikacji z możliwością serializacji.

Gdzie zachować stan

Istnieją typowe lokalizacje dla stanu utrwalania:

Magazyn po stronie serwera

W przypadku trwałej trwałości danych obejmującej wielu użytkowników i urządzeń aplikacja może używać magazynu po stronie serwera. Dostępne opcje:

  • Blob storage
  • Magazyn klucz-wartość
  • Relacyjna baza danych
  • Table Storage

Po zapisaniu danych stan użytkownika jest zachowywany i dostępny w każdym nowym obwodzie.

Aby uzyskać więcej informacji na temat opcji magazynu danych platformy Azure, zobacz następujące tematy:

URL

W przypadku przejściowych danych reprezentujących stan nawigacji modeluje dane jako część adresu URL. Przykłady stanu użytkownika modelowane w adresie URL to:

  • Identyfikator wyświetlanej jednostki.
  • Bieżący numer strony w siatce stronicowanej.

Zawartość paska adresu przeglądarki jest zachowywana:

  • Jeśli użytkownik ręcznie ponownie załaduje stronę.
  • Jeśli serwer internetowy stanie się niedostępny, a użytkownik musi ponownie załadować stronę w celu nawiązania połączenia z innym serwerem.

Aby uzyskać informacje na temat definiowania wzorców adresów URL za pomocą @page dyrektywy, zobacz ASP.NET Routing i nawigacja podstawowaBlazor.

Magazyn przeglądarki

W przypadku danych przejściowych, które użytkownik aktywnie tworzy, często używana lokalizacja magazynu to kolekcje i sessionStorage przeglądarkilocalStorage:

  • localStorage jest ograniczone do okna przeglądarki. Jeśli użytkownik ponownie załaduje stronę lub zamknie i ponownie otworzy przeglądarkę, stan będzie się powtarzać. Jeśli użytkownik otworzy wiele kart przeglądarki, stan zostanie udostępniony na kartach. Dane są utrwalane localStorage do momentu jawnego wyczyszczenia.
  • sessionStorage jest ograniczona do karty przeglądarki. Jeśli użytkownik ponownie załaduje kartę, stan będzie się powtarzać. Jeśli użytkownik zamknie kartę lub przeglądarkę, stan zostanie utracony. Jeśli użytkownik otworzy wiele kart przeglądarki, każda karta ma własną niezależną wersję danych.

Ogólnie rzecz biorąc, sessionStorage jest bezpieczniejszy do użycia. sessionStorage pozwala uniknąć ryzyka, że użytkownik otwiera wiele kart i napotyka następujące elementy:

  • Usterki w magazynie stanu na kartach.
  • Mylące zachowanie, gdy karta zastępuje stan innych kart.

localStorage jest lepszym wyborem, jeśli aplikacja musi zachować stan podczas zamykania i ponownego otwierania przeglądarki.

Zastrzeżenia dotyczące korzystania z magazynu przeglądarki:

  • Podobnie jak w przypadku korzystania z bazy danych po stronie serwera, ładowanie i zapisywanie danych jest asynchroniczne.
  • W przeciwieństwie do bazy danych po stronie serwera magazyn nie jest dostępny podczas prerenderingu, ponieważ żądana strona nie istnieje w przeglądarce podczas etapu wstępnego przetwarzania.
  • Przechowywanie kilku kilobajtów danych jest uzasadnione w przypadku aplikacji po stronie Blazor serwera. Poza kilkoma kilobajtami należy wziąć pod uwagę implikacje dotyczące wydajności, ponieważ dane są ładowane i zapisywane w sieci.
  • Użytkownicy mogą wyświetlać lub modyfikować dane. ASP.NET Core Data Protection może ograniczyć ryzyko. Na przykład ASP.NET Core Protected Browser Storage używa ASP.NET Core Data Protection.

Pakiety NuGet innych firm udostępniają interfejsy API do pracy z usługami localStorage i sessionStorage. Warto rozważyć wybranie pakietu, który w sposób niewidoczny używa ASP.NET Core Data Protection. Usługa Data Protection szyfruje przechowywane dane i zmniejsza potencjalne ryzyko naruszenia przechowywanych danych. Jeśli JSdane serializowane w postaci serializowanej są przechowywane w postaci zwykłego tekstu, użytkownicy mogą wyświetlać dane przy użyciu narzędzi deweloperskich przeglądarki, a także modyfikować przechowywane dane. Zabezpieczanie danych nie zawsze jest problemem, ponieważ dane mogą być proste w naturze. Na przykład odczytywanie lub modyfikowanie przechowywanego koloru elementu interfejsu użytkownika nie jest istotnym zagrożeniem bezpieczeństwa dla użytkownika lub organizacji. Unikaj zezwalania użytkownikom na inspekcję lub manipulowanie poufnymi danymi.

ASP.NET Core Protected Browser Storage

ASP.NET Core Protected Browser Storage korzysta z usługi ASP.NET Core Data Protection for localStorage i sessionStorage.

Uwaga

Usługa Protected Browser Storage korzysta z ASP.NET Core Data Protection i jest obsługiwana tylko w przypadku aplikacji po stronie Blazor serwera.

Ostrzeżenie

Microsoft.AspNetCore.ProtectedBrowserStorage to nieobsługiwany, eksperymentalny pakiet, który nie jest przeznaczony do użytku produkcyjnego.

Pakiet jest dostępny tylko do użycia w aplikacjach platformy ASP.NET Core 3.1.

Konfigurowanie

  1. Dodaj odwołanie do pakietu do Microsoft.AspNetCore.ProtectedBrowserStorage.

    Uwaga

    Aby uzyskać instrukcje dodawania pakietów do aplikacji .NET, zobacz artykuły w sekcji Instalowanie pakietów i zarządzanie nimi w temacie Przepływ pracy użycia pakietów (dokumentacja programu NuGet). Sprawdź prawidłowe wersje pakietów pod adresem NuGet.org.

  2. _Host.cshtml W pliku dodaj następujący skrypt wewnątrz tagu zamykającego</body>:

    <script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>
    
  3. W Startup.ConfigureServicespliku wywołaj metodę AddProtectedBrowserStorage , aby dodać localStorage usługi i sessionStorage do kolekcji usług:

    services.AddProtectedBrowserStorage();
    

Zapisywanie i ładowanie danych w składniku

W każdym składniku, który wymaga ładowania lub zapisywania danych w magazynie przeglądarki, użyj @inject dyrektywy , aby wstrzyknąć wystąpienie jednego z następujących elementów:

  • ProtectedLocalStorage
  • ProtectedSessionStorage

Wybór zależy od lokalizacji przechowywania przeglądarki, której chcesz użyć. W poniższym przykładzie sessionStorage jest używany:

@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore

Dyrektywę @using można umieścić w pliku aplikacji _Imports.razor zamiast w składniku. _Imports.razor Użycie pliku sprawia, że przestrzeń nazw jest dostępna dla większych segmentów aplikacji lub całej aplikacji.

Aby zachować currentCount wartość w Counter składniku aplikacji na Blazor podstawie szablonu projektu, zmodyfikuj metodę IncrementCount , aby użyć metody ProtectedSessionStore.SetAsync:

private async Task IncrementCount()
{
    currentCount++;
    await ProtectedSessionStore.SetAsync("count", currentCount);
}

W większych, bardziej realistycznych aplikacjach przechowywanie poszczególnych pól jest mało prawdopodobne. Aplikacje są bardziej prawdopodobne, aby przechowywać całe obiekty modelu, które obejmują stan złożony. ProtectedSessionStore automatycznie serializuje i deserializuje JSdane ON w celu przechowywania złożonych obiektów stanu.

W poprzednim przykładzie currentCount kodu dane są przechowywane w sessionStorage['count'] przeglądarce użytkownika. Dane nie są przechowywane w postaci zwykłego tekstu, ale są chronione przy użyciu ASP.NET Core Data Protection. Zaszyfrowane dane można sprawdzić, jeśli sessionStorage['count'] są oceniane w konsoli dewelopera przeglądarki.

Aby odzyskać currentCount dane, jeśli użytkownik wróci do Counter składnika później, w tym jeśli użytkownik znajduje się w nowym obwodzie, użyj polecenia ProtectedSessionStore.GetAsync:

protected override async Task OnInitializedAsync()
{
    var result = await ProtectedSessionStore.GetAsync<int>("count");
    currentCount = result.Success ? result.Value : 0;
}
protected override async Task OnInitializedAsync()
{
    currentCount = await ProtectedSessionStore.GetAsync<int>("count");
}

Jeśli parametry składnika obejmują stan nawigacji, wywołaj ProtectedSessionStore.GetAsync i przypisznull wynik OnParametersSetAsyncinny niż , a nie OnInitializedAsync. OnInitializedAsync jest wywoływany tylko raz, gdy składnik jest najpierw tworzone wystąpienie. OnInitializedAsync nie zostanie ponownie wywołana później, jeśli użytkownik przejdzie do innego adresu URL, pozostając na tej samej stronie. Aby uzyskać więcej informacji, zobacz Cykl życia składników platformy ASP.NET Core Razor.

Ostrzeżenie

Przykłady w tej sekcji działają tylko wtedy, gdy serwer nie ma włączonego prerenderingu. Po włączeniu prerenderingu jest generowany błąd wyjaśniający, że nie można wydać wywołań międzyoperacyjnych języka JavaScript, ponieważ składnik jest wstępnie wstępnie wypełniony.

Wyłącz prerendering lub dodaj dodatkowy kod, aby pracować z prerenderingiem. Aby dowiedzieć się więcej na temat pisania kodu, który działa z prerenderingiem, zobacz sekcję Obsługa prerenderingu .

Obsługa stanu ładowania

Ponieważ dostęp do magazynu przeglądarki jest uzyskiwany asynchronicznie za pośrednictwem połączenia sieciowego, zawsze istnieje okres czasu przed załadowaniem i udostępnieniem danych składnikowi. Aby uzyskać najlepsze wyniki, renderowanie komunikatu podczas ładowania jest w toku zamiast wyświetlania pustych lub domyślnych danych.

Jednym z podejść jest śledzenie, czy dane to null, co oznacza, że dane są nadal ładowane. W składniku domyślnym Counter liczba jest przechowywana w elemencie int. Ustaw currentCount wartość null , dodając znak zapytania (?) do typu (int):

private int? currentCount;

Zamiast bezwarunkowo wyświetlać liczbę i Increment przycisk, wyświetl te elementy tylko wtedy, gdy dane są ładowane, sprawdzając polecenie HasValue:

@if (currentCount.HasValue)
{
    <p>Current count: <strong>@currentCount</strong></p>
    <button @onclick="IncrementCount">Increment</button>
}
else
{
    <p>Loading...</p>
}

Obsługa prerenderingu

Podczas prerenderingu:

  • Interakcyjne połączenie z przeglądarką użytkownika nie istnieje.
  • Przeglądarka nie ma jeszcze strony, na której można uruchomić kod JavaScript.

localStorage lub sessionStorage nie są dostępne podczas prerenderingu. Jeśli składnik próbuje wchodzić w interakcję z magazynem, zostanie wygenerowany błąd wyjaśniający, że nie można wydać wywołań międzyoperacyjności języka JavaScript, ponieważ składnik jest wstępnie wypełniony.

Jednym ze sposobów usunięcia błędu jest wyłączenie prerenderingu. Zazwyczaj jest to najlepszy wybór, jeśli aplikacja korzysta z magazynu opartego na przeglądarce. Wstępne przetwarzanie zwiększa złożoność i nie przynosi korzyści aplikacji, ponieważ aplikacja nie może wstępnie prerender żadnej przydatnej zawartości, dopóki localStorage nie będzie dostępna.sessionStorage

Aby wyłączyć prerendering, wskaż tryb renderowania z parametrem prerender ustawionym na false najwyższy poziom składnika w hierarchii składników aplikacji, który nie jest składnikiem głównym.

Uwaga

Tworzenie składnika głównego interakcyjnego, takiego jak App składnik, nie jest obsługiwane. W związku z tym prerendering nie może być wyłączony bezpośrednio przez App składnik.

W przypadku aplikacji opartych na szablonie Blazor projektu aplikacja internetowa wstępne jest zwykle wyłączone, gdy Routes składnik jest używany w składniku App (Components/App.razor):

<Routes @rendermode="new InteractiveServerRenderMode(prerender: false)" />

Ponadto wyłącz prerendering dla HeadOutlet składnika:

<HeadOutlet @rendermode="new InteractiveServerRenderMode(prerender: false)" />

Aby uzyskać więcej informacji, zobacz tryby renderowania ASP.NET CoreBlazor.

Aby wyłączyć prerendering, otwórz _Host.cshtml plik i zmień render-mode atrybut pomocnika tagów składnika na Server:

<component type="typeof(App)" render-mode="Server" />

Po wyłączeniu prerenderingu prerendering zawartości jest wyłączona<head>.

Wstępne przetwarzanie może być przydatne w przypadku innych stron, które nie używają localStorage ani sessionStorage. Aby zachować wstępne ładowanie, odrocz operację ładowania, dopóki przeglądarka nie zostanie połączona z obwodem. Poniżej przedstawiono przykład przechowywania wartości licznika:

@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedLocalStorage ProtectedLocalStore

@if (isConnected)
{
    <p>Current count: <strong>@currentCount</strong></p>
    <button @onclick="IncrementCount">Increment</button>
}
else
{
    <p>Loading...</p>
}

@code {
    private int currentCount;
    private bool isConnected;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            isConnected = true;
            await LoadStateAsync();
            StateHasChanged();
        }
    }

    private async Task LoadStateAsync()
    {
        var result = await ProtectedLocalStore.GetAsync<int>("count");
        currentCount = result.Success ? result.Value : 0;
    }

    private async Task IncrementCount()
    {
        currentCount++;
        await ProtectedLocalStore.SetAsync("count", currentCount);
    }
}
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedLocalStorage ProtectedLocalStore

@if (isConnected)
{
    <p>Current count: <strong>@currentCount</strong></p>
    <button @onclick="IncrementCount">Increment</button>
}
else
{
    <p>Loading...</p>
}

@code {
    private int currentCount = 0;
    private bool isConnected = false;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            isConnected = true;
            await LoadStateAsync();
            StateHasChanged();
        }
    }

    private async Task LoadStateAsync()
    {
        currentCount = await ProtectedLocalStore.GetAsync<int>("count");
    }

    private async Task IncrementCount()
    {
        currentCount++;
        await ProtectedLocalStore.SetAsync("count", currentCount);
    }
}

Uwzględnianie zachowania stanu w wspólnej lokalizacji

Jeśli wiele składników korzysta z magazynu opartego na przeglądarce, implementowanie kodu dostawcy stanu wiele razy powoduje duplikowanie kodu. Jedną z opcji unikania duplikowania kodu jest utworzenie składnika nadrzędnego dostawcy stanu, który hermetyzuje logikę dostawcy stanu. Składniki podrzędne mogą pracować z utrwalanymi danymi bez względu na mechanizm trwałości stanu.

W poniższym przykładzie CounterStateProvider składnika dane licznika są utrwalane na :sessionStorage

@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore

@if (isLoaded)
{
    <CascadingValue Value="this">
        @ChildContent
    </CascadingValue>
}
else
{
    <p>Loading...</p>
}

@code {
    private bool isLoaded;

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    public int CurrentCount { get; set; }

    protected override async Task OnInitializedAsync()
    {
        var result = await ProtectedSessionStore.GetAsync<int>("count");
        CurrentCount = result.Success ? result.Value : 0;
        isLoaded = true;
    }

    public async Task SaveChangesAsync()
    {
        await ProtectedSessionStore.SetAsync("count", CurrentCount);
    }
}
@using Microsoft.AspNetCore.ProtectedBrowserStorage
@inject ProtectedSessionStorage ProtectedSessionStore

@if (isLoaded)
{
    <CascadingValue Value="this">
        @ChildContent
    </CascadingValue>
}
else
{
    <p>Loading...</p>
}

@code {
    private bool isLoaded;

    [Parameter]
    public RenderFragment ChildContent { get; set; }

    public int CurrentCount { get; set; }

    protected override async Task OnInitializedAsync()
    {
        CurrentCount = await ProtectedSessionStore.GetAsync<int>("count");
        isLoaded = true;
    }

    public async Task SaveChangesAsync()
    {
        await ProtectedSessionStore.SetAsync("count", CurrentCount);
    }
}

Uwaga

Aby uzyskać więcej informacji na temat RenderFragmentprogramu , zobacz składniki ASP.NET CoreRazor.

Składnik CounterStateProvider obsługuje fazę ładowania, nie renderując zawartości podrzędnej do momentu zakończenia ładowania stanu.

Aby stan był dostępny dla wszystkich składników w aplikacji, zawijaj CounterStateProvider składnik wokół Router składnika (<Router>...</Router>) za Routes pomocą globalnego interaktywnego renderowania po stronie serwera (interakcyjneJ usługi SSR).

W składniku App (Components/App.razor):

<Routes @rendermode="InteractiveServer" />

W składniku Routes (Components/Routes.razor):

Aby użyć CounterStateProvider składnika, opakuj wystąpienie składnika wokół dowolnego innego składnika, który wymaga dostępu do stanu licznika. Aby udostępnić stan wszystkim składnikom w aplikacji, opakuj CounterStateProvider składnik w Router składniku App (App.razor):

<CounterStateProvider>
    <Router ...>
        ...
    </Router>
</CounterStateProvider>

Uwaga

Od wydania wersji ASP.NET Core 5.0.1 i w przypadku wszelkich dodatkowych wydań 5.x składnik Router zawiera parametr PreferExactMatches ustawiony na wartość @true. Aby uzyskać więcej informacji, zobacz Migracja z platformy ASP.NET Core w wersji 3.1 do wersji 5.0.

Opakowane składniki odbierają i mogą modyfikować stan utrwalonego licznika. Następujący Counter składnik implementuje wzorzec:

@page "/counter"

<p>Current count: <strong>@CounterStateProvider?.CurrentCount</strong></p>
<button @onclick="IncrementCount">Increment</button>

@code {
    [CascadingParameter]
    private CounterStateProvider? CounterStateProvider { get; set; }

    private async Task IncrementCount()
    {
        if (CounterStateProvider is not null)
        {
            CounterStateProvider.CurrentCount++;
            await CounterStateProvider.SaveChangesAsync();
        }
    }
}

Poprzedni składnik nie jest wymagany do interakcji z ProtectedBrowserStorageprogramem ani nie zajmuje się fazą "ładowania".

Aby radzić sobie z prerenderingiem zgodnie z wcześniejszym opisem, można zmienić tak, CounterStateProvider aby wszystkie składniki, które korzystają z danych licznika, działały automatycznie z prerenderingiem. Aby uzyskać więcej informacji, zobacz sekcję Obsługa prerenderingu .

Ogólnie rzecz biorąc, zalecany jest wzorzec składnika nadrzędnego dostawcy stanu:

  • Aby korzystać ze stanu wielu składników.
  • Jeśli istnieje tylko jeden obiekt stanu najwyższego poziomu do utrwalania.

Aby utrwalać wiele różnych obiektów stanu i używać różnych podzbiorów obiektów w różnych miejscach, lepiej jest uniknąć utrwalania stanu globalnego.

Stan użytkownika utworzony w Blazor WebAssembly aplikacji jest przechowywany w pamięci przeglądarki.

Przykłady stanu użytkownika przechowywanego w pamięci przeglądarki:

Gdy użytkownik zamknie i ponownie otworzy przeglądarkę lub ponownie załaduje stronę, stan użytkownika przechowywany w pamięci przeglądarki zostanie utracony.

Uwaga

Usługa Protected Browser Storage (Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage przestrzeń nazw) opiera się na ASP.NET Core Data Protection i jest obsługiwana tylko w przypadku aplikacji po stronie Blazor serwera.

Utrwalanie stanu między sesjami przeglądarki

Ogólnie rzecz biorąc, należy zachować stan między sesjami przeglądarki, w których użytkownicy aktywnie tworzą dane, a nie tylko odczytywanie danych, które już istnieją.

Aby zachować stan między sesjami przeglądarki, aplikacja musi utrwalać dane w innej lokalizacji magazynu niż pamięć przeglądarki. Trwałość stanu nie jest automatyczna. Należy wykonać kroki podczas tworzenia aplikacji w celu zaimplementowania stanowej trwałości danych.

Trwałość danych jest zwykle wymagana tylko w przypadku stanu o wysokiej wartości, który użytkownicy wydali nakład pracy na utworzenie. W poniższych przykładach stan utrwalania pozwala zaoszczędzić czas lub pomoc w działaniach komercyjnych:

  • Formularze wieloetapowe sieci Web: czasochłonne jest ponowne wprowadzanie danych przez użytkownika w celu wykonania kilku wykonanych kroków wieloetapowego formularza internetowego, jeśli ich stan zostanie utracony. Użytkownik utraci stan w tym scenariuszu, jeśli odejdzie od formularza i wróci później.
  • Koszyki na zakupy: każdy składnik aplikacji, który reprezentuje potencjalny przychód, może być utrzymywany. Użytkownik, który traci swój stan, a tym samym koszyk zakupów, może zakupić mniej produktów lub usług po powrocie do witryny później.

Aplikacja może utrwalać tylko stan aplikacji. Interfejsy użytkownika nie mogą być utrwalane, takie jak wystąpienia składników i ich drzewa renderowania. Składniki i drzewa renderowania nie są zwykle serializowalne. Aby utrwalyć stan interfejsu użytkownika, taki jak rozwinięte węzły kontrolki widoku drzewa, aplikacja musi używać niestandardowego kodu do modelowania zachowania stanu interfejsu użytkownika jako stanu aplikacji z możliwością serializacji.

Gdzie zachować stan

Istnieją typowe lokalizacje dla stanu utrwalania:

Magazyn po stronie serwera

W przypadku trwałej trwałości danych obejmującej wielu użytkowników i urządzeń aplikacja może używać niezależnego magazynu po stronie serwera dostępnego za pośrednictwem internetowego interfejsu API. Dostępne opcje:

  • Blob storage
  • Magazyn klucz-wartość
  • Relacyjna baza danych
  • Table Storage

Po zapisaniu danych stan użytkownika jest zachowywany i dostępny w każdej nowej sesji przeglądarki.

Ponieważ Blazor WebAssembly aplikacje działają w całości w przeglądarce użytkownika, wymagają dodatkowych środków w celu uzyskania dostępu do bezpiecznych systemów zewnętrznych, takich jak usługi magazynu i bazy danych. Aplikacje zestawu Blazor WebAssembly są zabezpieczane w taki sam sposób jak aplikacje jednostronicowe. Zazwyczaj aplikacja uwierzytelnia użytkownika za pośrednictwem protokołu OAuth/OpenID Połączenie (OIDC), a następnie wchodzi w interakcje z usługami i bazami danych magazynu za pośrednictwem wywołań internetowego interfejsu API do aplikacji po stronie serwera. Aplikacja po stronie serwera pośredniczy w transferze danych między aplikacją Blazor WebAssembly a usługą magazynu lub bazą danych. Aplikacja Blazor WebAssembly utrzymuje efemeryczne połączenie z aplikacją po stronie serwera, podczas gdy aplikacja po stronie serwera ma trwałe połączenie z magazynem.

Aby uzyskać więcej informacji, zobacz następujące zasoby:

Aby uzyskać więcej informacji na temat opcji magazynu danych platformy Azure, zobacz następujące tematy:

URL

W przypadku przejściowych danych reprezentujących stan nawigacji modeluje dane jako część adresu URL. Przykłady stanu użytkownika modelowane w adresie URL to:

  • Identyfikator wyświetlanej jednostki.
  • Bieżący numer strony w siatce stronicowanej.

Zawartość paska adresu przeglądarki jest zachowywana, jeśli użytkownik ręcznie ponownie załaduje stronę.

Aby uzyskać informacje na temat definiowania wzorców adresów URL za pomocą @page dyrektywy, zobacz ASP.NET Routing i nawigacja podstawowaBlazor.

Magazyn przeglądarki

W przypadku danych przejściowych, które użytkownik aktywnie tworzy, często używana lokalizacja magazynu to kolekcje i sessionStorage przeglądarkilocalStorage:

  • localStorage jest ograniczone do okna przeglądarki. Jeśli użytkownik ponownie załaduje stronę lub zamknie i ponownie otworzy przeglądarkę, stan będzie się powtarzać. Jeśli użytkownik otworzy wiele kart przeglądarki, stan zostanie udostępniony na kartach. Dane są utrwalane localStorage do momentu jawnego wyczyszczenia.
  • sessionStorage jest ograniczona do karty przeglądarki. Jeśli użytkownik ponownie załaduje kartę, stan będzie się powtarzać. Jeśli użytkownik zamknie kartę lub przeglądarkę, stan zostanie utracony. Jeśli użytkownik otworzy wiele kart przeglądarki, każda karta ma własną niezależną wersję danych.

Uwaga

localStorage i sessionStorage może być używany w Blazor WebAssembly aplikacjach, ale tylko przez pisanie kodu niestandardowego lub przy użyciu pakietu innej firmy.

Ogólnie rzecz biorąc, sessionStorage jest bezpieczniejszy do użycia. sessionStorage pozwala uniknąć ryzyka, że użytkownik otwiera wiele kart i napotyka następujące elementy:

  • Usterki w magazynie stanu na kartach.
  • Mylące zachowanie, gdy karta zastępuje stan innych kart.

localStorage jest lepszym wyborem, jeśli aplikacja musi zachować stan podczas zamykania i ponownego otwierania przeglądarki.

Ostrzeżenie

Użytkownicy mogą wyświetlać lub modyfikować dane przechowywane w localStorage usługach i sessionStorage.

Usługa kontenera stanu w pamięci

Zagnieżdżone składniki zwykle wiążą dane przy użyciu powiązania łańcuchowego zgodnie z opisem w ASP.NET Powiązania danych CoreBlazor. Zagnieżdżone i niezagnieżdżone składniki mogą udostępniać dostęp do danych przy użyciu zarejestrowanego kontenera stanu w pamięci. Niestandardowa klasa kontenera stanu może używać atrybutu do Action powiadamiania składników w różnych częściach aplikacji o zmianach stanu. W poniższym przykładzie:

  • Para składników używa kontenera stanu do śledzenia właściwości.
  • Jeden składnik w poniższym przykładzie jest zagnieżdżony w drugim składniku, ale zagnieżdżanie nie jest wymagane, aby to podejście działało.

Ważne

W przykładzie w tej sekcji pokazano, jak utworzyć usługę kontenera stanu w pamięci, zarejestrować usługę i użyć jej w składnikach. W tym przykładzie dane nie są utrwalane bez dalszego programowania. W przypadku trwałego przechowywania danych kontener stanu musi przyjąć podstawowy mechanizm magazynu, który przetrwa po wyczyszczonej pamięci przeglądarki. Można to osiągnąć za pomocą localStorage/sessionStorage lub innej technologii.

StateContainer.cs:

public class StateContainer
{
    private string? savedString;

    public string Property
    {
        get => savedString ?? string.Empty;
        set
        {
            savedString = value;
            NotifyStateChanged();
        }
    }

    public event Action? OnChange;

    private void NotifyStateChanged() => OnChange?.Invoke();
}

Aplikacje po stronie klienta (Program plik):

builder.Services.AddSingleton<StateContainer>();

Aplikacje po stronie serwera (Program plik, ASP.NET Core na platformie .NET 6 lub nowszym):

builder.Services.AddScoped<StateContainer>();

Aplikacje po stronie serwera (Startup.ConfigureServices z Startup.csASP.NET Core wcześniejsze niż 6.0):

services.AddScoped<StateContainer>();

Shared/Nested.razor:

@implements IDisposable
@inject StateContainer StateContainer

<h2>Nested component</h2>

<p>Nested component Property: <b>@StateContainer.Property</b></p>

<p>
    <button @onclick="ChangePropertyValue">
        Change the Property from the Nested component
    </button>
</p>

@code {
    protected override void OnInitialized()
    {
        StateContainer.OnChange += StateHasChanged;
    }

    private void ChangePropertyValue()
    {
        StateContainer.Property = 
            $"New value set in the Nested component: {DateTime.Now}";
    }

    public void Dispose()
    {
        StateContainer.OnChange -= StateHasChanged;
    }
}

StateContainerExample.razor:

@page "/state-container-example"
@implements IDisposable
@inject StateContainer StateContainer

<h1>State Container Example component</h1>

<p>State Container component Property: <b>@StateContainer.Property</b></p>

<p>
    <button @onclick="ChangePropertyValue">
        Change the Property from the State Container Example component
    </button>
</p>

<Nested />

@code {
    protected override void OnInitialized()
    {
        StateContainer.OnChange += StateHasChanged;
    }

    private void ChangePropertyValue()
    {
        StateContainer.Property = "New value set in the State " +
            $"Container Example component: {DateTime.Now}";
    }

    public void Dispose()
    {
        StateContainer.OnChange -= StateHasChanged;
    }
}

Powyższe składniki implementują IDisposableelement , a OnChange delegaty są anulowane w Dispose metodach, które są wywoływane przez platformę podczas usuwania składników. Aby uzyskać więcej informacji, zobacz Cykl życia składników platformy ASP.NET Core Razor.

Dodatkowe podejścia

Podczas implementowania niestandardowego magazynu stanu przydatne jest zastosowanie kaskadowych wartości i parametrów:

  • Aby korzystać ze stanu wielu składników.
  • Jeśli istnieje tylko jeden obiekt stanu najwyższego poziomu do utrwalania.

Rozwiązywanie problemów

W niestandardowej usłudze zarządzania stanem wywołanie zwrotne wywoływane poza Blazorkontekstem synchronizacji programu musi opakowować logikę wywołania zwrotnego, ComponentBase.InvokeAsync aby przenieść je do kontekstu synchronizacji modułu renderowania.

Gdy usługa zarządzania stanem nie wywołuje StateHasChangedBlazorkontekstu synchronizacji, zgłaszany jest następujący błąd:

System.InvalidOperationException: "Bieżący wątek nie jest skojarzony z dyspozytorem. Użyj metody InvokeAsync(), aby przełączyć wykonywanie na dyspozytor podczas wyzwalania renderowania lub stanu składnika.

Aby uzyskać więcej informacji i przykład sposobu rozwiązywania tego błędu, zobacz ASP.NET Renderowanie składnika CoreRazor.

Dodatkowe zasoby