Blazor zarządzanie stanem podstawowego ASP.NET
Uwaga
Nie jest to najnowsza wersja tego artykułu. Aby zapoznać się z bieżącą wersją, zobacz wersję tego artykułu platformy .NET 9.
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ą, zobacz wersję tego artykułu platformy .NET 9.
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 localStorage
przeglądarkisessionStorage
:
-
localStorage
jest powiązana z instancją 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ą utrwalanelocalStorage
do momentu jawnego wyczyszczenia. DanelocalStorage
dokumentu załadowane w sesji "przeglądania prywatnego" lub "trybie incognito" są czyszczone po zamknięciu ostatniej karty "prywatnej". -
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.
- Żądana strona nie istnieje w przeglądarce podczas prerenderingu, więc magazyn lokalny nie jest dostępny podczas prerenderingu.
- 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 dane serializowane w formacie JSON 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 trywialnych nie jest problemem. 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
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.
_Host.cshtml
W pliku dodaj następujący skrypt wewnątrz tagu zamykającego</body>
:<script src="_content/Microsoft.AspNetCore.ProtectedBrowserStorage/protectedBrowserStorage.js"></script>
W
Startup.ConfigureServices
pliku wywołaj metodęAddProtectedBrowserStorage
, aby dodaćlocalStorage
usługi isessionStorage
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 dane JSON 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 Web App projektu prerendering jest zwykle wyłączony, 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 Blazor renderowania ASP.NET Core.
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" />
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 dla wspólnego dostawcy
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 składnika CounterStateProvider
dane licznika są zapisywane do sessionStorage
, a faza ładowania jest obsługiwana poprzez nierealizowanie renderowania zawartości podrzędnej do zakończenia ładowania stanu.
Składnik CounterStateProvider
zajmuje się prerenderowaniem, nie ładując stanu aż do momentu po renderowaniu składnika w metodzie cyklu życia OnAfterRenderAsync
, która nie jest wykonywana podczas prerenderowania.
Podejście opisane w tej sekcji nie jest w stanie wyzwolić ponownego renderowania wielu związanych komponentów na tej samej stronie. Jeśli jeden z subskrybowanych składników zmienia stan, ponownie się renderuje i może wyświetlać zaktualizowany stan, natomiast inny składnik na tej samej stronie, wyświetlający ten stan, pokazuje nieaktualne dane do czasu swojego kolejnego ponownego renderowania. W związku z tym podejście opisane w tej sekcji najlepiej nadaje się do używania stanu w jednym składniku na stronie.
CounterStateProvider.razor
:
@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 OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
isLoaded = true;
await LoadStateAsync();
StateHasChanged();
}
}
private async Task LoadStateAsync()
{
var result = await ProtectedSessionStore.GetAsync<int>("count");
CurrentCount = result.Success ? result.Value : 0;
isLoaded = true;
}
public async Task IncrementCount()
{
CurrentCount++;
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 OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
isLoaded = true;
await LoadStateAsync();
StateHasChanged();
}
}
private async Task LoadStateAsync()
{
CurrentCount = await ProtectedSessionStore.GetAsync<int>("count");
isLoaded = true;
}
public async Task IncrementCount()
{
CurrentCount++;
await ProtectedSessionStore.SetAsync("count", CurrentCount);
}
}
Uwaga
Aby uzyskać więcej informacji na temat RenderFragmentprogramu , zobacz Razor.
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)
{
await CounterStateProvider.IncrementCount();
}
}
}
Poprzedni składnik nie jest wymagany do interakcji z ProtectedBrowserStorage
programem ani nie zajmuje się fazą "ładowania".
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:
- 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).
- Wartości ustawiane za pomocą wywołań międzyoperacyjnych języka JavaScript.
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 Connect (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:
- Wywoływanie internetowego interfejsu API z aplikacji ASP.NET Core Blazor
- Zabezpieczanie ASP.NET Core Blazor WebAssembly
- Blazor Zabezpieczenia i Identity artykuły
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 localStorage
przeglądarkisessionStorage
:
-
localStorage
jest przypisany do instancji 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ą utrwalanelocalStorage
do momentu jawnego wyczyszczenia. DanelocalStorage
dokumentu załadowanego w sesji "przeglądania prywatnego" lub "incognito" są czyszczone po zamknięciu ostatniej karty "prywatnej". -
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 Blazor danych Core. 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.cs
ASP.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 przypadku korzystania z niestandardowej usługi zarządzania stanem, w której chcesz obsługiwać modyfikacje stanu spoza Blazorkontekstu synchronizacji (na przykład z czasomierza lub usługi w tle), wszystkie składniki zużywające muszą opakowować wywołanie StateHasChanged w ComponentBase.InvokeAsync. Dzięki temu powiadomienie o zmianie jest obsługiwane w kontekście synchronizacji programu renderatora.
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 Razor składnika Core.
Dodatkowe zasoby
- Zapisz stan aplikacji przed operacją uwierzytelniania (Blazor WebAssembly)
- Zarządzanie stanem za pośrednictwem zewnętrznego interfejsu API serwera