Blazor Render Modes
Os Blazor render modes, introduzidos no .NET 8, permitem escolher como cada página ou componente é desenhado, e isso mudou completamente a forma de pensar aplicações com Blazor.
Por que os Blazor render modes mudaram o jogo
Se você me perguntasse sobre o Blazor alguns anos atrás, eu diria que era legal, mas que ainda preferia o Angular. Afinal, trocar um framework consolidado por uma tecnologia nova não parecia uma boa ideia.
O tempo passou e o Blazor amadureceu. Mesmo assim, minha recomendação era usar o Blazor principalmente quando você já tinha código em C#.
Isso porque, mesmo trabalhando no modelo SPA, o Blazor, assim como Angular, React e Vue, não tinha um bom suporte a SSR. Na prática, é o SSR que sustenta o SEO de um site.
No caso do balta, por exemplo, o Blazor seria incrível na área de compras, trazendo mais interatividade para o usuário.
Da mesma forma, no Player, que é a área onde o aluno assiste aos cursos, o Blazor seria devastador. Ainda assim, todo o nosso site roda em Razor Pages.
Essa decisão aconteceu porque nossa aplicação é relativamente pequena. Portanto, não havia motivo para quebrá-la em três partes só para ter o Blazor no pagamento e no Player.
Muitos negócios se seguraram nesses argumentos. No entanto, com os Blazor render modes, o jogo mudou completamente.
O que são Blazor render modes
No Blazor do .NET 8, a Microsoft incluiu uma opção chamada render mode, ou modo de renderização, para componentes e páginas.
Essa opção define o modelo de renderização de uma página ou de um componente. Além disso, você também pode aplicar um único modo para a aplicação inteira.
Particularmente, opto pela flexibilidade. Então, só escolheria um único render mode do Blazor para a aplicação toda em um cenário muito específico.
Segundo a documentação oficial de render modes do ASP.NET Core, todo componente de um Blazor Web App adota um modo que define onde ele roda e se ele é interativo.
Os quatro modos disponíveis
Ao todo, temos quatro modos de renderização. Cada um tem suas particularidades, que afetam a forma como páginas e componentes aparecem na tela:
- Static SSR, com a variação Streaming Rendering
- Interactive Server
- WebAssembly interativo (Interactive WebAssembly)
- Auto interativo (Interactive Auto)
O Static SSR roda no servidor e não tem interatividade. Já o Interactive Server também roda no servidor, mas mantém uma conexão em tempo real com o navegador.
O Interactive WebAssembly, por sua vez, roda no cliente. Por fim, o Interactive Auto começa no servidor e passa para o cliente depois que o navegador baixa o runtime do .NET.
Static SSR no Blazor
Já comentamos sobre o SSR, ou Server Side Rendering. Agora, vamos entender na prática o que ele é e como ele funciona.
Como funciona o processo de SEO
Uma das validações que o Google faz para indexar e ranquear nossas páginas é verificar o título e o conteúdo de cada uma.
Quando buscamos por ".NET", por exemplo, o buscador verifica as páginas com esse termo no título. Em seguida, ele também analisa o corpo da página.
Essa última etapa é a mais complexa para nós. Afinal, se a página não tiver um corpo condizente com o título, ela despenca no ranking.
Vale lembrar que, no modelo WASM puro, o HTML final nasce no navegador. Em outras palavras, o servidor entrega uma página quase vazia para o robô de busca.
Além disso, os render modes funcionam por componente ou página. Então, seja criativo, porque nem todo componente precisa de SSR.
Como usar SSR nos Blazor render modes
Lembra do exemplo em que trabalhamos no Counter e adicionamos a diretiva @rendermode InteractiveServer? Ela é justamente o que torna um componente interativo.
Se sua página ou componente não declara de forma explícita que é interativo, o Blazor automaticamente usa o SSR estático. Entre os Blazor render modes, esse é o comportamento padrão.
Tomando como base a página Weather do template, que carrega dados de um serviço, podemos comentar o atributo StreamRendering ou passar false como parâmetro.
@* @attribute [StreamRendering(true)] *@
@* ou *@
@attribute [StreamRendering(false)]
Porém, ao inspecionar a página, vemos que os dados estão no HTML. Ou seja, se esse conteúdo fosse relevante para o SEO, estaríamos bem.
Nesse cenário, o servidor gera toda a página, ou seja, HTML, CSS e JS, e só depois envia o resultado para o navegador.
É o mesmo processo que o ASP.NET Razor Pages executa. Talvez seja hora de migrar o balta para Blazor.
Streaming rendering
O Streaming Rendering executa um processo parecido, porém mais inteligente. Primeiro, ele entrega a página e, em seguida, envia os dados.
Para utilizá-lo, basta aplicar o atributo StreamRendering(true) ao componente, conforme o exemplo abaixo.
@attribute [StreamRendering(true)]
No .NET 8, o parâmetro true é obrigatório. A partir do .NET 9, o valor padrão passou a ser true, então o atributo sem parâmetro também funciona.
Para visualizar o efeito com clareza, recomendo aumentar o atraso na recuperação dos dados dentro do componente Weather.
// Linha 45 do Weather.razor
await Task.Delay(5000);
Essa página simula um cenário comum: ler dados lentos de um banco ou serviço.
Ao executar a aplicação e navegar para a página de clima, o conteúdo aparece, mas os dados ainda não. Enquanto isso, a tela exibe uma mensagem de Loading.
A principal diferença para o SSR estático é o tempo de resposta. No exemplo anterior, a página só aparecia quando os dados estavam completos.
Neste caso, assim que os dados chegam do banco ou do serviço, eles vão para a tela e atualizam o componente ou a página.
Porém, se você precisa dessas informações para uma boa indexação, é melhor olhar o HTML antes de decidir.
Como podemos ver, os dados entraram depois do carregamento da página. Por isso, o HTML de origem continua com o conteúdo inicial.
Dessa forma, o HTML sempre mostra o conteúdo que o servidor enviou na primeira resposta via SSR.
<h1>Weather</h1>
<p>This component demonstrates showing data.</p><p><em>Loading...</em></p><!--/bl:13--></article></main></div>
Como essa mágica acontece
Sempre que o Blazor identifica um componente com StreamRendering, ele renderiza tudo o que já tem em mãos.
No caso da página Weather, ele ainda não tem os dados. Então, ele envia a página e continua aguardando as informações adicionais.
Assim que os dados chegam, o Blazor atualiza a tela, porque a página continua conectada ao servidor.
De acordo com a documentação de renderização de componentes, o servidor envia o conteúdo atualizado pela mesma conexão da resposta e aplica as mudanças no DOM.
Ao inspecionar a execução da página, vemos um item de desconexão depois que todos os dados chegam.
Isso significa que, mesmo com a página na tela, ainda havia uma conexão ativa com o servidor. Ela só fechou momentos depois.
Vale um alerta: hosts que fazem buffer da resposta anulam esse efeito. Nesse caso, a página simplesmente carrega sem o streaming.
Interactive Server nos Blazor render modes
Enquanto o SSR e o Streaming Rendering são excelentes para exibir informações, ainda precisamos de interação.
Sempre que queremos dar vida aos componentes, com botões, inputs e outros elementos, temos três opções nos Blazor render modes. A primeira é o Interactive Server.
Nesse formato, adicionamos a diretiva @rendermode InteractiveServer ao componente ou à página.
O comportamento lembra o do Streaming Rendering. Ou seja, o componente aparece na tela, mas continua conectado ao servidor.
Dessa forma, o código roda no servidor, que usa o SignalR sobre WebSocket para manter a conexão aberta.
O componente responde e se atualiza quase instantaneamente. Isso acontece porque a conexão via socket e a otimização na transferência de dados ajudam bastante.
Porém, assim como no Streaming Rendering, modificamos os elementos depois que o HTML chega ao navegador. Por isso, o código fonte da página não muda.
Além disso, os modos interativos usam prerendering por padrão. Assim, o servidor entrega uma versão estática primeiro e, depois, ativa a interatividade.
Criando projetos no Visual Studio
Quando escrevi a primeira versão deste conteúdo, a versão mais recente do .NET 8 era a RC 2. Por isso, criamos os projetos pelo Visual Studio Preview.
Hoje, o Visual Studio 2022 versão 17.8 ou superior já traz o template. Além disso, o comando dotnet new blazor aceita a opção --interactivity com os valores None, Server, WebAssembly e Auto.
# cria um Blazor Web App com interatividade Auto
dotnet new blazor -o BlazorAppAuto --interactivity Auto
No Visual Studio, selecione a opção File, New Project e busque pelo template Blazor Web App.
Em seguida, a opção Interactivity Type define o tipo de interação.
Também temos o Interactivity Location, que pode ser Global, para todas as páginas e componentes, ou Per page/component, para cada página ou componente.
Vale ressaltar que o Identity também chegou ao Blazor. Assim, podemos configurar a autenticação como None ou Individual Accounts.
Por fim, temos os tipos de interação. Por enquanto, vamos ignorar None e Server, que já vimos, e focar em WebAssembly e Auto.
Nos próximos tópicos, usamos como base projetos criados com Interactivity Type WebAssembly e Auto, respectivamente.
Interactive WebAssembly
Mas, se podemos rodar o .NET direto no navegador com WebAssembly, por que usar o modo anterior?
Por exemplo, não daria para guardar uma connection string no WASM, porque ela ficaria exposta no navegador.
Por outro lado, entre os Blazor render modes, o Interactive Server exige mais infraestrutura e pode ser mais lento.
Além de não depender do servidor, o WASM aproveita a capacidade da máquina do cliente. Se quiser se aprofundar, leia sobre WASM e o futuro da web.
Inspecionando o projeto WebAssembly
Para iniciar um projeto com Interactivity Type em WebAssembly, basta selecionar a opção conforme a imagem abaixo.
Com esses Blazor render modes, o template cria dois projetos: servidor e cliente.
No Program.cs do servidor, BlazorApp1, aparece o AddInteractiveWebAssemblyComponents.
Esse item só aparece em projetos com Interactivity Type definido como WebAssembly ou Auto.
Além disso, como o WebAssembly roda apenas no navegador, precisamos de um projeto dedicado para ele, o BlazorApp1.Client.
// ...
builder
.Services
.AddRazorComponents()
.AddInteractiveWebAssemblyComponents();
// ...
app.MapRazorComponents<App>()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(Counter).Assembly);
Já o AddAdditionalAssemblies(typeof(Counter).Assembly) indica onde encontrar os componentes do tipo WebAssembly.
Notamos também que, na pasta Components/Pages do projeto BlazorApp1, o componente Counter.razor não existe mais.
Essa mudança acontece porque, em um projeto com suporte a WebAssembly, o template move o componente de exemplo para o BlazorApp1.Client.
Todos os componentes desse projeto podem rodar em WebAssembly. Ainda assim, o projeto servidor também pode usá-los.
O Program.cs do BlazorApp1.Client é extremamente simples.
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
await builder.Build().RunAsync();
Por fim, na pasta Pages do Client, encontramos o Counter.razor, cujo código só muda na diretiva de render mode.
@page "/counter"
@rendermode InteractiveWebAssembly
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
Desta vez, como mostra a segunda linha, usamos InteractiveWebAssembly no lugar do InteractiveServer que aplicamos antes.
Executando o projeto WebAssembly
O projeto BlazorApp1.Client serve apenas como local para guardar as páginas e os componentes que queremos rodar em WebAssembly.
Por isso, sempre executamos o projeto BlazorApp1, que carrega o BlazorApp1.Client consigo.
O mesmo vale para a publicação. Ou seja, fazemos o deploy do BlazorApp1, que é a aplicação, e não do BlazorApp1.Client.
Como resultado, temos o mesmo exemplo dos tópicos anteriores. Porém, agora o Counter.razor roda via WebAssembly, com C# executando no navegador.
No primeiro carregamento, notamos uma leve lentidão, porque o navegador baixa o runtime do .NET.
Auto nos Blazor render modes
Finalizando os Blazor render modes, temos o Auto, que mescla Server e WebAssembly.
Nesse modo, o componente começa no modelo Server, já que essa é a forma mais rápida de exibi-lo.
Dizemos isso porque, no WASM, o navegador precisa baixar o .NET. Esse download pode levar algum tempo, dependendo da conexão do usuário.
Depois que o componente aparece na tela, o Blazor inicia o download do runtime do .NET em segundo plano.
Nas próximas visitas, o Blazor verifica se o runtime já está no navegador. Em caso positivo, o componente roda em WebAssembly, o que o torna mais performático.
Um detalhe importante: o Auto nunca troca o modo de um componente que já está na página. Ele decide uma vez e mantém essa escolha enquanto o componente existir.
Em resumo, no Auto temos o melhor dos dois mundos, com agilidade na primeira renderização e o desempenho do WASM depois.
Inspecionando o projeto Auto
Para iniciar um projeto Auto, selecione a opção abaixo.
Esse projeto combina dois Blazor render modes, Server e WASM.
Desta vez, porém, o Program.cs usa tanto o AddInteractiveServerComponents quanto o AddInteractiveWebAssemblyComponents.
// ...
builder
.Services
.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
// ...
app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(Counter).Assembly);
Da mesma forma, precisamos do AddInteractiveServerRenderMode e do AddInteractiveWebAssemblyRenderMode para suportar os dois modos.
Executando o projeto Auto
A execução acontece pelo projeto BlazorAppAuto, e não pelo BlazorAppAuto.Client, igual ao exemplo anterior.
Como resultado, temos um comportamento idêntico ao anterior, com o componente Counter na tela.
Porém, existe um detalhe sutil. Na primeira execução, o componente rodou no modo Server.
Enquanto o usuário navegava pela aplicação, o navegador baixou o runtime do .NET em segundo plano.
Agora, sempre que o usuário voltar ao Counter, o componente roda em WASM, já que o runtime está no navegador.
Ao analisar o Counter.razor, vemos a diretiva InteractiveAuto no lugar de InteractiveServer ou InteractiveWebAssembly.
@page "/counter"
@rendermode InteractiveAuto
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
Como escolher entre os Blazor render modes
Em resumo, temos praticamente o mesmo código em todos os modos. No entanto, cada um se comporta de forma bem diferente por baixo dos panos.
Para páginas de conteúdo que dependem de SEO, o Static SSR é a escolha natural. Afinal, o HTML completo sai do servidor.
Quando a página carrega dados lentos e você quer mostrar o layout rápido, o Streaming Rendering melhora a experiência do usuário.
Se o componente precisa de interação e acesso seguro a recursos do servidor, o Interactive Server resolve sem expor segredos.
Já o Interactive WebAssembly faz sentido quando você quer aliviar o servidor e usar o poder de processamento do cliente.
Por fim, o Auto equilibra velocidade inicial e desempenho posterior. Para entender melhor a base do framework, veja também uma visão geral do Blazor.
Conclusão sobre Blazor render modes
Os Blazor render modes eliminaram o principal argumento contra o Blazor em sites públicos, que era a falta de SSR.
Com eles, uma mesma aplicação pode ter páginas estáticas para SEO, áreas de compra interativas e um Player rodando no navegador.
Portanto, aquele cenário do balta, que exigiria três aplicações, hoje cabe em um único Blazor Web App. Experimente combinar os modos e escolha o melhor para cada componente.
FAQ
Qual é o render mode padrão de um componente no Blazor Web App?
Quando o componente não declara nenhuma diretiva @rendermode, ele usa o Static SSR. Ou seja, o servidor gera o HTML e não existe interatividade.
O Streaming Rendering ajuda no SEO?
Só parcialmente, porque o HTML inicial contém apenas o conteúdo provisório. Se os dados carregados depois forem importantes para o SEO, prefira o SSR sem streaming.
Onde ficam os componentes que usam WebAssembly ou Auto?
Eles ficam no projeto que termina com .Client. O projeto servidor referencia esse assembly com AddAdditionalAssemblies.
O modo Auto troca de Server para WebAssembly com o componente aberto?
Não, ele decide o modo uma única vez para cada componente. A troca para WebAssembly só acontece em renderizações futuras, depois que o runtime estiver em cache.
Posso usar render modes no Blazor Server ou Blazor WebAssembly antigos?
Não, os render modes pertencem aos Blazor Web Apps a partir do .NET 8. Os modelos de hospedagem anteriores continuam funcionando com seus próprios conceitos.