Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Без сохранения состояния компонента состояние, используемое во время предварительной подготовки, будет потеряно и должно быть повторно создано при полной загрузке приложения. Если какое-либо состояние создается асинхронно, пользовательский интерфейс может мерцать при замене предварительно отрендеренного интерфейса во время перерисовки компонента.
Рассмотрим следующий PrerenderedCounter1 компонент счетчика. Компонент задает начальное случайное значение счетчика при предварительном рендеринге в OnInitialized методе жизненного цикла. При интерактивном отображении компонента начальное значение счетчика заменяется, когда OnInitialized выполняется во второй раз.
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++;
}
Замечание
Если приложение принимает интерактивную маршрутизацию, а страница достигается с помощью внутренней расширенной навигации, предварительное рендеринг не выполняется. Поэтому необходимо выполнить полную перезагрузку страницы для PrerenderedCounter1 компонента, чтобы просмотреть следующие выходные данные. Для получения дополнительной информации см. раздел Интерактивная маршрутизация и предварительная отрисовка.
Запустите приложение и проверьте журналирование компонента. Ниже приведен пример выходных данных.
info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 41
info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 92
Первое зарегистрированное количество происходит во время предварительной прорисовки. Счетчик устанавливается снова после предварительной отрисовки, когда компонент перерисовывается. Также происходит мерцание в пользовательском интерфейсе, когда число обновляется с 41 до 92.
Чтобы удерживать начальное значение счетчика во время предварительной отрисовки, Blazor поддерживает сохранение состояния на предварительно отрисованных страницах с помощью службы PersistentComponentState (а для компонентов, внедренных в страницы или представления Razor приложений Pages или MVC, — с помощью вспомогательного тега Persist Component State).
При инициализации компонентов с тем же состоянием, которое использовалось во время предварительной отрисовки, все ресурсоемкие шаги инициализации выполняются только один раз. Отрисованный пользовательский интерфейс также соответствует предварительно отрисованному пользовательскому интерфейсу, поэтому в браузере нет никаких мерцаний.
Сохраняемое предварительно созданное состояние передается клиенту, где оно используется для восстановления состояния компонента. Во время рендеринга на стороне клиента (CSR InteractiveWebAssembly) данные доступны в браузере и не должны содержать конфиденциальную информацию. Во время интерактивной отрисовки на стороне сервера (интерактивная служба SSR) InteractiveServerASP.NET Core Data Protection гарантирует безопасное передачу данных. Режим рендеринга InteractiveAuto объединяет взаимодействие WebAssembly и сервера, поэтому необходимо внимательно оценить возможность воздействия данных на браузер, как и в случае с CSR.
Чтобы сохранить предварительно созданное состояние, используйте [PersistentState] атрибут для сохранения состояния в свойствах. Свойства с этим атрибутом автоматически сохраняются с помощью службы PersistentComponentState при предпросмотре. Состояние извлекается при интерактивном рендеринге компонента или при создании экземпляра службы.
По умолчанию свойства сериализуются с помощью System.Text.Json сериализатора с параметрами по умолчанию и сохраняются в предопределенном HTML- коде. Сериализация не является безопасной и требует сохранения используемых типов. Дополнительные сведения см. в статье Настройка средства обрезки для ASP.NET Core Blazor.
Следующий компонент счетчика сохраняет состояние счетчика во время предварительной подготовки и извлекает состояние для инициализации компонента:
-
Атрибут
[PersistentState]применяется к типу nullint(CurrentCount). - Состояние счетчика назначается, когда
nullнаходится вOnInitialized, и восстанавливается автоматически при интерактивной отрисовке компонента.
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++;
}
При выполнении компонента CurrentCount устанавливается только один раз во время предварительного рендеринга. Значение восстанавливается при повторной отрисовке компонента. Ниже приведен пример выходных данных.
Замечание
Если приложение принимает интерактивную маршрутизацию, а страница достигается с помощью внутренней расширенной навигации, предварительное рендеринг не выполняется. Поэтому необходимо выполнить полную перезагрузку страницы для компонента, чтобы просмотреть следующие выходные данные. Для получения дополнительной информации см. раздел Интерактивная маршрутизация и предварительная отрисовка.
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount restored to 96
В следующем примере, который демонстрирует сериализацию состояния для нескольких компонентов одного типа:
- Свойства, аннотированные атрибутом
[PersistentState], сериализуются во время предварительной подготовки. -
Атрибут
@keyдирективы используется для обеспечения правильности связи состояния с экземпляром компонента. - Свойство
Elementинициализируется в методеOnInitializedжизненного цикла, чтобы избежать исключений нулевой ссылки, аналогично тому, как предотвращаются нулевые ссылки для параметров запроса и данных формы.
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" />
}
Сериализация состояния для служб
В следующем примере, который сериализует состояние для службы внедрения зависимостей:
- Свойства, аннотированные атрибутом
[PersistentState], сериализуются во время предварительной отрисовки и десериализации, когда приложение становится интерактивным. - Метод-расширение RegisterPersistentService используется для регистрации службы для постоянного хранения. Режим отрисовки является обязательным, так как режим отрисовки не может быть выведен из типа службы. Используйте любое из следующих значений:
-
RenderMode.Server: Услуга доступна для режима рендеринга Интерактивного Сервера. -
RenderMode.Webassembly: Услуга доступна для режима интерактивной отрисовки WebAssembly. -
RenderMode.InteractiveAuto: служба доступна для режимов отрисовки интерактивного сервера и интерактивного вебассембли, если компонент отрисовывается в любом из этих режимов.
-
- Служба инициализируется во время запуска интерактивного режима рендеринга, и свойства, аннотированные атрибутом
[PersistentState], десериализуются.
Замечание
Поддерживаются только службы с заданной областью действия, которые сохраняются.
Сериализованные свойства определяются из фактического экземпляра службы:
- Такой подход позволяет пометить абстракцию как устойчивую службу.
- Позволяет фактическим реализациям быть внутренними или разными типами.
- Поддерживает общий код в разных сборках.
- Приводит к тому, что каждый экземпляр обнаруживает одинаковые свойства.
Следующая служба счетчиков CounterTracker помечает свое текущее свойство счетчика CurrentCount атрибутом [PersistentState]. Свойство сериализуется во время предварительной отрисовки и десериализуется, когда приложение становится интерактивным в любом месте, где служба внедрена.
CounterTracker.cs:
public class CounterTracker
{
[PersistentState]
public int CurrentCount { get; set; }
public void IncrementCount()
{
CurrentCount++;
}
}
Program В файле зарегистрируйте службу с областью действия и зарегистрируйте службу для сохраняемости.RegisterPersistentService В следующем примере служба CounterTracker доступна как для режима интерактивного сервера, так и для интерактивного режима WebAssembly, если компонент отрисовывается в любом из этих режимов, поскольку зарегистрирована в RenderMode.InteractiveAuto.
Program Если файл еще не использует пространство имен Microsoft.AspNetCore.Components.Web, добавьте в начало файла следующую инструкцию using:
using Microsoft.AspNetCore.Components.Web;
Где службы зарегистрированы в Program файле:
builder.Services.AddScoped<CounterTracker>();
builder.Services.AddRazorComponents()
.RegisterPersistentService<CounterTracker>(RenderMode.InteractiveAuto);
Внедрите сервис CounterTracker в компонент и используйте его для увеличения значения счетчика. Для демонстрационных целей в следующем примере значение свойства службы CurrentCount равно 10 только в процессе предварительного рендеринга.
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();
}
}
Чтобы использовать предыдущий компонент для демонстрации сохранения количества 10 в CounterTracker.CurrentCount, перейдите к компоненту и обновите браузер, который активирует предварительное отображение. Когда происходит предварительная отрисовка, вы кратко видите RendererInfo.Name с надписью "Static", прежде чем будет отображено "Server" после окончательной отрисовки. Счетчик начинается с 10.
Используйте службу PersistentComponentState непосредственно вместо декларативной модели.
В качестве альтернативы использованию декларативной модели для сохранения состояния с [PersistentState] атрибутом можно использовать PersistentComponentState службу напрямую, которая обеспечивает большую гибкость для сложных сценариев сохраняемости состояния. Вызовите PersistentComponentState.RegisterOnPersisting, чтобы зарегистрировать обратный вызов для сохранения состояния компонента во время предварительного рендеринга. Состояние извлекается при интерактивном отображении компонента. Выполните вызов в конце кода инициализации, чтобы избежать потенциального условия гонки во время завершения работы приложения.
Следующий пример компонента счетчика сохраняет состояние счетчика во время предварительной отрисовки и извлекает его для инициализации компонента.
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();
}
При выполнении компонента currentCount устанавливается только один раз во время предварительного рендеринга. Значение восстанавливается при повторной отрисовке компонента. Ниже приведен пример выходных данных.
Замечание
Если приложение принимает интерактивную маршрутизацию, а страница достигается с помощью внутренней расширенной навигации, предварительное рендеринг не выполняется. Поэтому необходимо выполнить полную перезагрузку страницы для компонента, чтобы просмотреть следующие выходные данные. Для получения дополнительной информации см. раздел Интерактивная маршрутизация и предварительная отрисовка.
info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount restored to 96
Чтобы сохранить предварительно созданное состояние, определите, какое состояние следует сохранить с помощью PersistentComponentState службы. PersistentComponentState.RegisterOnPersisting регистрирует обратный вызов для сохранения состояния компонента во время предварительной подготовки. Состояние извлекается при интерактивном отображении компонента. Выполните вызов в конце кода инициализации, чтобы избежать потенциального условия гонки во время завершения работы приложения.
Следующий пример компонента счетчика сохраняет состояние счетчика во время предварительной отрисовки и извлекает его для инициализации компонента.
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++;
}
При выполнении компонента currentCount устанавливается только один раз во время предварительного рендеринга. Значение восстанавливается при повторной отрисовке компонента. Ниже приведен пример выходных данных.
Замечание
Если приложение принимает интерактивную маршрутизацию, а страница достигается с помощью внутренней расширенной навигации, предварительное рендеринг не выполняется. Поэтому необходимо выполнить полную перезагрузку страницы для компонента, чтобы просмотреть следующие выходные данные. Для получения дополнительной информации см. раздел Интерактивная маршрутизация и предварительная отрисовка.
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount restored to 96
Расширяемость сериализации для состояния постоянного компонента
Реализуйте пользовательский сериализатор с помощью PersistentComponentStateSerializer<T>. Без зарегистрированного пользовательского сериализатора сериализация возвращается к существующей сериализации JSON.
Настраиваемый сериализатор регистрируется в файле приложения Program . В следующем примере CustomUserSerializer регистрируется для типа TUser:
builder.Services.AddSingleton<PersistentComponentStateSerializer<TUser>,
CustomUserSerializer>();
Тип автоматически сохраняется и восстанавливается с помощью пользовательского сериализатора:
[PersistentState]
public User? CurrentUser { get; set; } = new();
Компоненты, встроенные в страницы и представления (Razor Pages/MVC)
Для компонентов, встроенных на страницу или представление Razor в приложении Pages или MVC, необходимо добавить тег- помощник Persist Component State, включив HTML-тег <persist-component-state /> внутрь закрывающего тега </body> макета приложения. Это необходимо только для Razor приложений Pages и MVC. Для получения дополнительной информации см. раздел Вспомогательная функция Tag для сохранения состояния компонента в ASP.NET Core.
Pages/Shared/_Layout.cshtml:
<body>
...
<persist-component-state />
</body>
Интерактивная маршрутизация и предварительная отрисовка
Если компонент Routes не определяет режим отрисовки, приложение использует интерактивность и навигацию по страницам или компонентам. С помощью навигации по страницам или компонентам внутренняя навигация обрабатывается расширенной маршрутизацией после того, как приложение станет интерактивным. "Внутренняя навигация" в этом контексте означает, что URL-адрес события навигации является конечной Blazor точкой внутри приложения.
Blazor поддерживает обработку состояния постоянного компонента во время расширенной навигации. Состояние, сохраненное во время расширенной навигации, можно считывать интерактивными компонентами на странице.
По умолчанию состояние сохраняемого компонента загружается только интерактивными компонентами при первоначальной загрузке на странице. Это предотвращает перезапись важных состояний, таких как данные в редактируемой веб-форме, если дополнительные расширенные события навигации происходят на той же странице после загрузки компонента.
Если данные доступны только для чтения и часто не изменяются, вы можете разрешить обновления во время расширенной навигации, задав AllowUpdates = trueатрибут[PersistentState]. Это полезно для таких сценариев, как отображение кэшированных данных, которые очень дороги для получения, но часто не изменяются. В следующем примере показано использование данных прогноза AllowUpdates погоды:
[PersistentState(AllowUpdates = true)]
public WeatherForecast[]? Forecasts { get; set; }
protected override async Task OnInitializedAsync()
{
Forecasts ??= await ForecastService.GetForecastAsync();
}
Чтобы пропустить восстановление состояния во время предварительной отрисовки, установите значение RestoreBehaviorSkipInitialValue:
[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)]
public string NoPrerenderedData { get; set; }
Чтобы пропустить восстановление состояния во время повторного подключения, установите значение RestoreBehaviorSkipLastSnapshot. Это может быть полезно для обеспечения свежих данных после повторного подключения:
[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)]
public int CounterNotRestoredOnReconnect { get; set; }
Вызов PersistentComponentState.RegisterOnRestoring для регистрации обратного вызова для императивного управления восстановлением состояния, аналогично тому, как PersistentComponentState.RegisterOnPersisting обеспечивает полный контроль над сохранением состояния.
Служба PersistentComponentState работает только при первоначальной загрузке страницы и не действует при внутренних улучшенных событиях навигации по страницам.
Если приложение выполняет полную (не расширенную) навигацию на страницу, использующую состояние постоянного компонента, сохраняемое состояние становится доступным для приложения, когда оно становится интерактивным.
Если интерактивный контур уже установлен и осуществляется улучшенная навигация к странице, использующей состояние сохраняемого компонента, состояние недоступно в уже имеющемся контуре для его использования компонентом. Для внутреннего запроса страницы нет пререндеринга, а служба PersistentComponentState не знает, что произошла улучшенная навигация. Нет механизма доставки обновлений состояния компонентам, которые уже работают в существующем канале. Причиной этого является то, что Blazor поддерживает только передачу состояния с сервера клиенту во время инициализации среды выполнения, а не после запуска среды выполнения.
Отключение расширенной навигации, которая снижает производительность, однако также позволяет избежать проблемы загрузки состояния для PersistentComponentState внутренних запросов страниц, рассматривается в навигации ASP.NET CoreBlazor. Кроме того, обновите приложение до .NET 10 или более поздней версии, где Blazor поддерживает обработку состояния постоянного компонента при расширенной навигации.
ASP.NET Core