Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Sem persistir o estado do componente, o estado usado durante a pré-geração é perdido e deve ser recriado quando o aplicativo é totalmente carregado. Se qualquer estado for criado de maneira assíncrona, a interface do usuário poderá piscar à medida que a interface do usuário pré-renderizada for substituída quando o componente for renderizado novamente.
Considere o componente de contador PrerenderedCounter1 a seguir. O componente define um valor inicial de contador aleatório durante a pré-renderização no método do ciclo de vida OnInitialized é executado uma segunda vez.
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++;
}
Observação
Se o aplicativo adotar o roteamento interativo e a página for acessada por meio de uma navegação aprimorada interna, a pré-geração não ocorrerá. Portanto, você deve executar um recarregamento completo de página para que o componente PrerenderedCounter1 veja a saída a seguir. Para mais informações, consulte a seção Roteamento interativo e pré-renderização.
Execute o aplicativo e inspecione o log no componente. A seguir está a saída de exemplo:
info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 41
info: BlazorSample.Components.Pages.PrerenderedCounter1[0]
currentCount set to 92
A primeira contagem registrada ocorre durante a pré-renderização. A contagem é definida novamente após a pré-renderização quando o componente é renderizado novamente. Há também uma cintilação na interface do usuário quando a contagem é atualizada de 41 para 92.
Para manter o valor inicial do contador durante a pré-geração, Blazor dá suporte ao estado persistente em uma página pré-gerada usando o PersistentComponentState serviço (e para componentes inseridos em páginas ou exibições de Razor páginas ou aplicativos MVC, o Auxiliar de Marca de Estado do Componente Persistente).
Ao inicializar componentes com o mesmo estado usado durante a pré-renderização, todas as etapas de inicialização dispendiosa só são executadas uma vez. A interface do usuário renderizada também corresponde à interface do usuário pré-renderizada, portanto, nenhuma cintilação ocorre no navegador.
O estado pré-renderizado persistente é transferido para o cliente, onde é usado para restaurar o estado do componente. Durante a renderização do lado do cliente (CSR, InteractiveWebAssembly), os dados são expostos ao navegador e não devem conter informações confidenciais e privadas. Durante a renderização interativa do lado do servidor (SSR InteractiveServerinterativa), ASP.NET Core Data Protection garante que os dados sejam transferidos com segurança. O modo de renderização InteractiveAuto combina interatividade WebAssembly e Server, exigindo atenção à exposição de dados ao navegador, como no caso de CSR.
Para preservar o estado pré-gerado, use o atributo [PersistentState] para manter o estado nas propriedades. As propriedades com esse atributo são mantidas automaticamente usando o PersistentComponentState serviço durante a pré-geração. O estado é recuperado quando o componente é renderizado interativamente ou o serviço é instanciado.
Por padrão, as propriedades são serializadas usando o System.Text.Json serializador com configurações padrão e persistidas no HTML pré-gerado. A serialização não é segura para operações de corte e requer a preservação dos tipos usados. Para obter mais informações, consulte Configurar o Trimmer para ASP.NET Core Blazor.
O seguinte componente de contador persiste o estado do contador durante a pré-renderização e recupera o estado para inicializar o componente.
- O
[PersistentState]atributo é aplicado ao tipo anulávelint(CurrentCount). - O estado do contador é atribuído quando
nullemOnInitializede restaurado automaticamente quando o componente é renderizado de forma interativa.
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++;
}
Quando o componente é executado, a CurrentCount é definida apenas uma vez durante a pré-renderização. O valor é restaurado quando o componente é renderizado novamente. A seguir está a saída de exemplo:
Observação
Se o aplicativo adotar o roteamento interativo e a página for acessada por meio de uma navegação aprimorada interna, a pré-geração não ocorrerá. Portanto, você deve executar um recarregamento de página inteira para que o componente exiba a saída a seguir. Para mais informações, consulte a seção Roteamento interativo e pré-renderização.
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
CurrentCount restored to 96
No exemplo a seguir, que serializa o estado para vários componentes do mesmo tipo:
- As propriedades anotadas com o
[PersistentState]atributo são serializadas durante a pré-renderização. - O
@keyatributo de diretiva é usado para garantir que o estado esteja corretamente associado à instância do componente. - A
Elementpropriedade é inicializada no método doOnInitializedciclo de vida para evitar exceções de referências nulas, da mesma forma como as referências nulas são evitadas para parâmetros de consulta e dados de formulário.
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" />
}
Serializar o estado para serviços
No exemplo que se segue, que serializa o estado de um serviço de injeção de dependência:
- As propriedades anotadas com o
[PersistentState]atributo são serializadas durante a pré-geração e desserializadas quando o aplicativo se torna interativo. - O RegisterPersistentService método de extensão é usado para registrar o serviço para persistência. O modo de renderização é necessário porque o modo de renderização não pode ser inferido do tipo de serviço. Use qualquer um dos seguintes valores:
-
RenderMode.Server: o serviço está disponível para o modo de renderização do Servidor Interativo. -
RenderMode.Webassembly: O serviço está disponível para o modo de renderização Interativo Webassembly. -
RenderMode.InteractiveAuto: O serviço estará disponível para os modos de renderização do Servidor Interativo e WebAssembly Interativo se um componente for renderizado em qualquer um desses modos.
-
- O serviço é resolvido durante a inicialização de um modo de renderização interativo e as propriedades anotadas com o
[PersistentState]atributo são desserializadas.
Observação
Apenas a persistência de serviços com escopo definido é suportada.
As propriedades serializadas são identificadas pela instância real do serviço.
- Essa abordagem permite marcar uma abstração como um serviço persistente.
- Permite que as implementações reais sejam de tipos internos ou diferentes.
- Dá suporte a código compartilhado em assemblies diferentes.
- Os resultados em cada instância revelam as mesmas propriedades.
O serviço de contador a seguir, CounterTracker, marca sua propriedade de contagem atual, CurrentCount, com o atributo [PersistentState]. A propriedade é serializada durante a prérenderização e desserializada quando o aplicativo se torna interativo, independentemente do local onde o serviço é injetado.
CounterTracker.cs:
public class CounterTracker
{
[PersistentState]
public int CurrentCount { get; set; }
public void IncrementCount()
{
CurrentCount++;
}
}
No arquivo Program, registre o serviço com escopo e registre o serviço para persistência com RegisterPersistentService. No exemplo a seguir, o serviço CounterTracker estará disponível tanto para os modos de renderização do Servidor Interativo quanto para o WebAssembly Interativo, se um componente for renderizado em qualquer um desses modos, porque está registrado com RenderMode.InteractiveAuto.
Se o Program arquivo ainda não usar o Microsoft.AspNetCore.Components.Web namespace, adicione a seguinte using instrução à parte superior do arquivo:
using Microsoft.AspNetCore.Components.Web;
Onde os serviços são registrados no Program arquivo:
builder.Services.AddScoped<CounterTracker>();
builder.Services.AddRazorComponents()
.RegisterPersistentService<CounterTracker>(RenderMode.InteractiveAuto);
Injete o serviço CounterTracker em um componente e use-o para incrementar um contador. Para fins de demonstração no exemplo a seguir, o valor da propriedade do serviço CurrentCount é definido como 10 somente durante a pré-renderização.
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();
}
}
Para usar o componente anterior para demonstrar a persistência da contagem de 10 em CounterTracker.CurrentCount, navegue até o componente e atualize o navegador, o que dispara a pré-renderização. Quando ocorrer a pré-renderização, você verá brevemente RendererInfo.Name indicar "Static" antes de exibir "Server" após a renderização final. O contador começa em 10.
Use o PersistentComponentState serviço diretamente em vez do modelo declarativo
Como alternativa ao uso do modelo declarativo para manter o estado com o [PersistentState] atributo, você pode usar diretamente o PersistentComponentState serviço, que oferece maior flexibilidade para cenários complexos de persistência de estado. Chame PersistentComponentState.RegisterOnPersisting para registrar um callback para persistir o estado do componente durante a prerenderização. O estado é recuperado quando o componente é renderizado interativamente. Faça a chamada ao final do código de inicialização para evitar uma possível condição de corrida durante o desligamento do aplicativo.
O exemplo de componente de contador a seguir persiste o estado do contador durante a pré-renderização e recupera o estado para inicializar o componente.
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();
}
Quando o componente é executado, a currentCount é definida apenas uma vez durante a pré-renderização. O valor é restaurado quando o componente é renderizado novamente. A seguir está a saída de exemplo:
Observação
Se o aplicativo adotar o roteamento interativo e a página for acessada por meio de uma navegação aprimorada interna, a pré-geração não ocorrerá. Portanto, você deve executar um recarregamento de página inteira para que o componente exiba a saída a seguir. Para mais informações, consulte a seção Roteamento interativo e pré-renderização.
info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter3[0]
currentCount restored to 96
Para preservar o estado pré-renderizado, decida qual estado será persistido por meio do serviço PersistentComponentState. PersistentComponentState.RegisterOnPersisting registra um retorno de chamada para persistir o estado do componente durante a pré-renderização. O estado é recuperado quando o componente é renderizado interativamente. Faça a chamada ao final do código de inicialização para evitar uma possível condição de corrida durante o desligamento do aplicativo.
O exemplo de componente de contador a seguir persiste o estado do contador durante a pré-renderização e recupera o estado para inicializar o componente.
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++;
}
Quando o componente é executado, a currentCount é definida apenas uma vez durante a pré-renderização. O valor é restaurado quando o componente é renderizado novamente. A seguir está a saída de exemplo:
Observação
Se o aplicativo adotar o roteamento interativo e a página for acessada por meio de uma navegação aprimorada interna, a pré-geração não ocorrerá. Portanto, você deve executar um recarregamento de página inteira para que o componente exiba a saída a seguir. Para mais informações, consulte a seção Roteamento interativo e pré-renderização.
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount set to 96
info: BlazorSample.Components.Pages.PrerenderedCounter2[0]
currentCount restored to 96
Extensibilidade de serialização para o estado do componente persistente
Implementar um serializador personalizado com PersistentComponentStateSerializer<T>. Sem um serializador personalizado registrado, a serialização volta para a serialização JSON existente.
O serializador personalizado é registrado no arquivo do Program aplicativo. No exemplo a seguir, o registro CustomUserSerializer é para o tipo TUser.
builder.Services.AddSingleton<PersistentComponentStateSerializer<TUser>,
CustomUserSerializer>();
O tipo é mantido e restaurado automaticamente com o serializador personalizado:
[PersistentState]
public User? CurrentUser { get; set; } = new();
Componentes inseridos em páginas e exibições (Páginas/MVC Razor)
Para componentes inseridos em uma página ou exibição de um Razor aplicativo Pages ou MVC, você deve adicionar o Auxiliar de Marca de Estado do Componente Persist com a <persist-component-state /> marca HTML dentro da marca de fechamento </body> do layout do aplicativo. Isso só é necessário em aplicativos MVC e Razor Pages. Para obter mais informações, consulte Persist Component State Tag Helper in ASP.NET Core.
Pages/Shared/_Layout.cshtml:
<body>
...
<persist-component-state />
</body>
Roteamento interativo e pré-renderização
Quando o componente Routes não define um modo de renderização, o aplicativo está usando interatividade e navegação por página/componente. Usando a navegação por página/componente, a navegação interna é tratada pelo roteamento aprimorado depois que o aplicativo se torna interativo. "Navegação interna" nesse contexto significa que o destino da URL do evento de navegação é um Blazor ponto de extremidade dentro do aplicativo.
Blazor dá suporte ao tratamento do estado do componente persistente durante a navegação aprimorada. O estado persistente durante a navegação aprimorada pode ser lido por componentes interativos na página.
Por padrão, o estado do componente persistente só é carregado por componentes interativos quando eles são carregados inicialmente na página. Isso impede que o estado importante, como dados em um webform editado, seja substituído se eventos de navegação avançados adicionais para a mesma página ocorrerem depois que o componente for carregado.
Se os dados forem somente leitura e não forem alterados com frequência, opte por permitir atualizações durante a navegação aprimorada definindo AllowUpdates = true o [PersistentState] atributo. Isso é útil para cenários como exibir dados armazenados em cache que são caros de buscar, mas não mudam com frequência. O exemplo a seguir demonstra o uso de dados de previsão do AllowUpdates tempo:
[PersistentState(AllowUpdates = true)]
public WeatherForecast[]? Forecasts { get; set; }
protected override async Task OnInitializedAsync()
{
Forecasts ??= await ForecastService.GetForecastAsync();
}
Para ignorar o estado de restauração durante a pré-geração, defina RestoreBehavior como SkipInitialValue:
[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)]
public string NoPrerenderedData { get; set; }
Para ignorar o estado de restauração durante a reconexão, defina RestoreBehavior como SkipLastSnapshot. Isso pode ser útil para garantir novos dados após a reconexão:
[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)]
public int CounterNotRestoredOnReconnect { get; set; }
Chame PersistentComponentState.RegisterOnRestoring para registrar um retorno de chamada para controlar imperativamente como o estado é restaurado, semelhante a como PersistentComponentState.RegisterOnPersisting fornece controle total de como o estado é persistente.
O serviço PersistentComponentState funciona apenas na carga inicial da página e não em eventos de navegação de página avançados internos.
Se o aplicativo executar uma navegação completa (não aprimorada) para uma página que utiliza estado do componente persistente, o estado persistido fica disponível para o aplicativo usar quando ele se tornar interativo.
Se um circuito interativo já tiver sido estabelecido e uma navegação aprimorada for executada em uma página utilizando o estado do componente persistente, o estado não será disponibilizado no circuito existente para uso do componente. Não é feita nenhuma pré-renderização para a solicitação de página interna, e o serviço PersistentComponentState não está ciente de que uma navegação aprimorada tenha ocorrido. Não há mecanismo para fornecer atualizações de estado para componentes que já estão em execução em um circuito existente. O motivo disso é que Blazor só dá suporte à passagem de estado do servidor para o cliente no momento em que o runtime é inicializado, não após o runtime ter sido iniciado.
Desabilitar a navegação aprimorada, que reduz o desempenho, mas também evita o problema de carregar o estado com PersistentComponentState em solicitações de página internas, é abordado em navegação no ASP.NET CoreBlazor. Como alternativa, atualize o aplicativo para o .NET 10 ou posterior, onde Blazor dá suporte ao tratamento do estado do componente persistente durante a navegação aprimorada.