Bibliotecas de classes (RCLs) do ASP.NET Core Razor com renderização estática do lado do servidor (SSR estático)

Este artigo fornece diretrizes para autores de bibliotecas de componentes que estão considerando o suporte para renderização estática do lado do servidor (SSR estático).

A tecnologia de código aberto Blazor incentiva o desenvolvimento de um ecossistema de bibliotecas de componentes comerciais e de código aberto, formalmente chamadas de bibliotecas de classes Razor (RCLs). Os desenvolvedores também podem criar componentes reutilizáveis para compartilhar componentes de forma privada entre aplicativos dentro de suas próprias empresas. O ideal é que os componentes sejam desenvolvidos para serem compatíveis com o maior número possível de modelos de hospedagem e modos de renderização. O SSR estático introduz restrições adicionais que podem ser mais desafiadoras para dar suporte do que modos de renderização interativos.

Entenda as funcionalidades e restrições do SSR estático

O SSR estático é um modo no qual os componentes são executados quando o servidor trata de uma solicitação HTTP de entrada. BlazorO servidor renderiza o componente como o HTML, que será incluído na resposta. Uma vez enviada a resposta, o componente do lado do servidor e o estado da renderização são descartados, de modo que tudo o que resta é o HTML no navegador.

O benefício desse modo é uma hospedagem mais barata e escalonável, pois não são necessários recursos contínuos do servidor para manter o estado do componente, nenhuma conexão contínua deve ser mantida entre o navegador e o servidor e nenhum payload do WebAssembly é necessário no navegador.

Por padrão, todos os componentes existentes ainda podem ser usados com SSR estático. Entretanto, o custo desse modo é que os manipuladores de eventos, como @onclick†, não podem ser executados pelos motivos a seguir:

  • Não existe código .NET no navegador para executá-los.
  • O servidor descartou imediatamente qualquer componente e estado do renderizador que seria necessário para executar os manipuladores de eventos ou renderizar as mesmas instâncias de componentes.

Existe uma exceção especial para o manipulador de eventos @onsubmit para funções, que é sempre funcional, independentemente do modo de renderização.

Isso é equivalente ao comportamento dos componentes durante a pré-renderização, antes de um Blazor circuito ou runtime Blazor WebAssembly ser iniciado.

Para componentes cuja única função é produzir conteúdos DOM somente leitura, esses comportamentos para o SSR estático são completamente suficientes. Entretanto, a criação de bibliotecas deve considerar a abordagem a ser adotada ao incluir componentes interativos em suas bibliotecas.

Opções para a criação de componentes

Existem três abordagens principais:

  • Não usar comportamentos interativos (Básico)

    Para componentes cuja única função é produzir conteúdos DOM somente leitura, não é necessário que o desenvolvedor execute nenhuma ação especial. Esses componentes funcionam naturalmente com qualquer modo de renderização.

    Exemplos:

    • Um componente de "cartão de usuário" que carrega dados correspondentes a uma pessoa e os renderiza em uma interface do usuário estilizada com uma foto, cargo e outros detalhes.
    • Um componente de "vídeo" que atua como um invólucro em torno do elemento HTML <video>, tornando-o mais conveniente para uso em um componente Razor.
  • Requer a renderização interativa (Básico)

    Você pode optar por requerer que seu componente seja usado somente com a renderização interativa. Isso limita o aplicativo do seu componente, mas significa que você pode confiar livremente em manipuladores de eventos arbitrários. Mesmo assim, você deve evitar declarar um @rendermode específico e permitir que o autor do aplicativo que consome sua biblioteca selecione um.

    Exemplos:

    • Um componente de edição de vídeo no qual os usuários podem cortar e reordenar segmentos de vídeo. Mesmo que existisse uma maneira de representar essas operações de edição com botões HTML simples e postagens de formulário, a experiência do usuário seria inaceitável sem uma verdadeira interatividade.
    • Um editor de documentos colaborativo que precisa mostrar as atividades de outros usuários em tempo real.
  • Utilize comportamentos interativos, mas projete para o SSR estático e aprimoramento progressivo (Avançado)

    Muitos comportamentos interativos podem ser implementados usando apenas funcionalidades HTML. Com um bom reconhecimento do HTML e CSS, muitas vezes é possível produzir uma linha de base útil de funcionalidade que funcione com SSR estático. Você ainda pode declarar manipuladores de eventos que implementam comportamentos mais avançados e opcionais, que só funcionam em modos de renderização interativos.

    Exemplos:

    • Um componente de grade. No SSR estático, o componente pode dar suporte apenas para a exibição de dados e a navegação entre páginas (implementada com links <a>). Quando usado com a renderização interativa, o componente pode adicionar classificação e filtragem dinâmica.
    • Um componente de conjunto de guias. Desde que a navegação entre guias seja alcançada usando links <a> e o estado seja mantido apenas nos parâmetros de consulta da URL, o componente pode funcionar sem @onclick.
    • Um componente avançado de upload de arquivos. No SSR estático, o componente pode se comportar como um <input type=file> nativo. Quando usado com a renderização interativa, o componente também pode exibir o progresso do upload.
    • Um ticker de ações. No SSR estático, o componente pode exibir a cotação da ação no momento em que o HTML foi renderizado. Quando usado com renderização interativa, o componente pode atualizar o preço das ações em tempo real.

Para qualquer uma dessas estratégias, existe também a opção de implementar recursos interativos com JavaScript.

Para escolher entre essas abordagens, os autores de Razorcomponentes reutilizáveis devem fazer uma compensação entre custo e benefício. Seu componente será mais útil e terá uma base de usuários em potencial mais ampla se der suporte para todos os modos de renderização, incluindo SSR estático. Entretanto, é preciso mais trabalho para projetar e implementar um componente que dê suporte e aproveite ao máximo cada modo de renderização.

Quando usar a diretiva @rendermode

Na maioria dos casos, as criações de componentes reutilizáveis não devem especificar um modo de renderização, mesmo quando a interatividade for necessária. Isso ocorre porque o autor do componente não sabe se o aplicativo habilita o suporte para InteractiveServer, InteractiveWebAssembly ou ambos com InteractiveAuto. Ao não especificar @rendermode, o autor do componente deixa a escolha para o desenvolvedor do aplicativo.

Mesmo que o autor do componente pense que a interatividade é necessária, ainda podem existir casos em que o autor do aplicativo considere suficiente usar apenas o SSR estático. Por exemplo, um componente de mapa com interatividade de arrastar e ampliar pode parecer exigir interatividade. Entretanto, alguns cenários podem chamar apenas a renderização de uma imagem de mapa estática e evitar os recursos de arrastar/zoom.

O único motivo pelo qual uma criação de componente reutilizável deve usar a diretiva @rendermode em seu componente é se a implementação estiver fundamentalmente acoplada a um modo de renderização específico e certamente causaria um erro se fosse usada em um modo diferente. Considere um componente com o objetivo principal de interagir diretamente com o SO host usando APIs específicas do Windows ou do Linux. Talvez seja impossível usar esse componente no WebAssembly. Se for o caso, é razoável declarar @rendermode InteractiveServer para o componente.

Renderização de streaming

Os componentes Razor reutilizáveis podem declarar @attribute [StreamRendering] para renderização de streaming ([StreamRendering] API de atributo). Isso resulta em atualizações incrementais da interface do usuário durante o SSR estático. Como os mesmos padrões de carregamento de dados produzem atualizações incrementais da interface do usuário durante a renderização interativa, independentemente da presença do atributo [StreamRendering], o componente pode se comportar corretamente em todos os casos. Mesmo nos casos em que o SSR estático de streaming é suprimido no servidor, o componente ainda renderiza seu estado final correto.

Os componentes Razor reutilizáveis podem usar links e navegação aprimorada. As marcas HTML <a> devem produzir comportamentos equivalentes com ou sem um componente Router interativo e com a habilitação/desabilitação ou não da navegação aprimorada em um nível ancestral no DOM.

Uso de formulários em modos de renderização

Componentes reutilizáveis Razor podem incluir formulários (seja <form> ou <EditForm>), pois estes podem ser implementados para funcionar de forma equivalente em ambos os modos de renderização estática e interativa.

Considere o seguinte exemplo:

<EditForm Enhance FormName="NewProduct" Model="Model" OnValidSubmit="SaveProduct">
    <DataAnnotationsValidator />
    <ValidationSummary />

    <p>Name: <InputText @bind-Value="Item.Name" /></p>

    <button type="submit">Submit</button>
</EditForm>

@code {
    [SupplyParameterFromForm]
    public Product? Model { get; set; }

    protected override void OnInitialized() => Model ??= new();

    private async Task Save()
    {
        ...
    }
}

As APIs Enhance, FormName e SupplyParameterFromFormAttribute são usadas somente durante o SSR estático e são ignoradas durante a renderização interativa. O formulário funciona corretamente durante o SSR interativo e estático.

Evite APIs que sejam específicas do SSR estático

Para criar um componente reutilizável que funcione em todos os modos de renderização, não dependa de HttpContext porque ele só está disponível durante o SSR estático. Não faz sentido usar a API HttpContext durante a renderização interativa porque não existe nenhuma solicitação HTTP ativa em andamento nesses tempos. Não faz sentido pensar em definir um código de status ou escrever na resposta.

Os componentes reutilizáveis são livres para receber um HttpContext quando disponível, como segue:

[CascadingParameter]
public HttpContext? Context { get; set; }

O valor é null durante a renderização interativa e só é definido durante o SSR estático.

Em muitos casos, existem alternativas melhores do que usar HttpContext. Se precisar saber a URL atual ou realizar um redirecionamento, as APIs em NavigationManager funcionam com todos os modos de renderização. Se precisar saber o estado de autenticação do usuário, use o serviço AuthenticationStateProvider de Blazor em vez de usar HttpContext.User.