Pré-renderizar componentes Razor do ASP.NET Core

Este artigo explica os cenários de pré-renderização de componentes Razor para os componentes renderizados pelo servidor nos Aplicativos Web Blazor.

A pré-renderização é o processo de renderizar inicialmente o conteúdo da página no servidor sem habilitar manipuladores de eventos para controles renderizados. O servidor gera a interface do usuário HTML da página o mais rápido possível em resposta à solicitação inicial, o que dá a sensação de que o aplicativo é mais responsivo aos usuários. A pré-renderização também pode melhorar a SEO (Otimização do Mecanismo de Pesquisa) ao renderizar conteúdos de resposta ao HTTP inicial no quais os mecanismos de pesquisa usam para calcular a classificação da página.

Persistência do estado pré-renderizado

Sem a persistência do estado pré-renderizado, há perda do estado usado durante a pré-renderização e ele deve ser recriado quando o aplicativo for 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 de contador aleatório inicial durante a pré-renderização no método OnInitialized do ciclo de vida. Depois que a conexão de SignalR com o cliente é estabelecida, o componente é renderizado novamente e o valor de contagem inicial é substituído quando OnInitialized é executado uma segunda vez.

PrerenderedCounter1.razor:

@page "/prerendered-counter-1"
@rendermode @(new InteractiveServerRenderMode(prerender: true))
@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++;
    }
}

Execute o aplicativo e inspecione o log no componente. A seguir está a saída de exemplo:

Observação

Se o aplicativo adotar o roteamento interativo (aprimorado) e a página for acessada por meio de uma navegação interna, a pré-renderização não ocorrerá. Portanto, você deve executar um recarregamento completo da página para que o componente PrerenderedCounter1 veja a saída a seguir.

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 reter o valor inicial do contador durante a pré-renderização, o Blazor dá suporte ao estado persistente em uma página pré-renderizada por meio do serviço PersistentComponentState (e para os componentes incorporados em páginas ou exibições de aplicativos MVC ou Razor Pages, o Auxiliar de Marcas para Persistência do Estado de Componentes).

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 a persistência do estado do componente antes que o aplicativo seja pausado. O estado é recuperado com a retomada do aplicativo.

O seguinte exemplo demonstra o padrão geral:

  • O espaço reservado {TYPE} representa o tipo de dados a ser persistido.
  • O espaço reservado {TOKEN} é uma cadeia de caracteres de identificador de estado. Considere usar nameof({VARIABLE}), em que o espaço reservado {VARIABLE} é o nome da variável que mantém o estado. Usar nameof() para o identificador de estado evita o uso de uma cadeia de caracteres entre aspas.
@implements IDisposable
@inject PersistentComponentState ApplicationState

...

@code {
    private {TYPE} data;
    private PersistingComponentStateSubscription persistingSubscription;

    protected override async Task OnInitializedAsync()
    {
        persistingSubscription = 
            ApplicationState.RegisterOnPersisting(PersistData);

        if (!ApplicationState.TryTakeFromJson<{TYPE}>(
            "{TOKEN}", out var restored))
        {
            data = await ...;
        }
        else
        {
            data = restored!;
        }
    }

    private Task PersistData()
    {
        ApplicationState.PersistAsJson("{TOKEN}", data);

        return Task.CompletedTask;
    }

    void IDisposable.Dispose()
    {
        persistingSubscription.Dispose();
    }
}

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()
    {
        persistingSubscription =
            ApplicationState.RegisterOnPersisting(PersistCount);

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

    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 interna, a pré-renderização não ocorrerá. Portanto, você deve executar um recarregamento completo de página para que o componente PrerenderedCounter2 veja a saída a seguir.

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

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.

Componentes inseridos em páginas e exibições (Páginas/MVC Razor)

Para os componentes incorporados em uma página ou em uma exibição de um aplicativo MVC ou Razor Pages, adicione o Auxiliar de Marcas para Persistência do Estado de Componentes com a marca HTML <persist-component-state /> dentro da marca </body> de fechamento do layout do aplicativo. Isso só é necessário em aplicativos MVC e Razor Pages. Para obter mais informações, confira Auxiliar de Marcas para Persistência do Estado de Componentes no ASP.NET Core.

Pages/Shared/_Layout.cshtml:

<body>
    ...

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

Roteamento interativo e pré-renderização

A navegação interna para roteamento interativo não envolve a solicitação de novo conteúdo de página do servidor. Portanto, a pré-renderização não ocorre para solicitações de páginas internas.

O serviço PersistentComponentState só funciona no carregamento inicial da página e não em eventos de navegação de página aprimorados. 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. Mas se um circuito interativo já foi estabelecido e uma navegação aprimorada é executada para uma página que renderiza estado do componente persistido, esse estado fica disponível no circuito existente. O serviço PersistentComponentState não reconhece a navegação aprimorada e não há nenhum mecanismo para fornecer atualizações de estado aos componentes que já estão em execução.

Diretrizes de pré-renderização

As diretrizes de pré-renderização são organizadas na documentação do Blazor por assunto. Os seguintes links abrangem todas as diretrizes de pré-renderização em toda a documentação definidas por assunto: