Pré-renderização de aplicativos Blazor - Como funciona - dicas e truques
Pré-renderização de aplicativos Blazor - Como funciona - dicas e truques
O que é pré-renderização?
Antes de mais nada, temos que esclarecer o que significa "pré-renderização" no Blazor. Por que alguém faria isso? Além disso, isso se aplica ao Blazor Client e ao Blazor Server?
Como seria uma solicitação inicial normal sem pré-renderização?
- Nosso usuário acessa nossa página. Assim, nosso navegador irá para o endereço fornecido e carregará nosso arquivo de índice.
- Nosso servidor agora servirá coisas diferentes dependendo se usarmos Cliente ou Servidor º Cliente: Estamos servindo um blazor.webassembly.js que inicializa o download de seus assemblies de aplicação e assemblies .NET que são então compilados para WebAssembly e executados º Servidor: Estamos servindo um blazor.server.js* que cria uma conexão SignalR do seu navegador para o servidor web. A conexão SignalR basicamente despacha suas entradas para o servidor e retorna o novo "HTML-Diff"
- A versão inicializada renderizará seu componente e o exibirá para o usuário
Qual é o problema aqui? Temos 2 problemas. Um para o blazor do lado do cliente e outro para o lado do servidor:
- Lado do cliente: Antes que o usuário possa interagir com seu site, todo o conteúdo deve ser baixado. Isso pode demorar um pouco
- Lado do cliente / Lado do servidor: Como precisamos de JavaScript e SignalR ou WebAssembly, você terá dificuldade em otimizar seu site para os mecanismos de pesquisa. Eles muitas vezes não executam JavaScript ou terão problemas com WebSockets / WebAssembly.
A solução: Pré-renderização Pré-renderização significa apenas que pegamos a primeira solicitação e fingimos ser como um site ASP.NET Core normal. Nós apenas renderizamos tudo e retornaremos o conteúdo (html estático) para o cliente (ao lado das outras coisas descritas acima). É assim que se parece:
Benefícios
- Para o blazor do lado do cliente, mostramos o site inicial ao usuário sem baixar o pacote .NET "grande". Assim, o usuário tem uma experiência muito mais suave
- Para ambos: Habilitamos o SEO.
Como habilitar isso?
Pois é super fácil. Você vai para o seu _Host.cshtml e adiciona o render-mode como o seguinte
<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
Para o lado do cliente/WebAssembly
<component type="typeof(App)" render-mode="ServerPrerendered" />
Para o lado do servidor Blazor.
A desvantagem
Agora o diagrama está mostrando dois "displays" / "renders", certo? Sim. Dê uma olhada no seguinte componente:
@page "/"
@inject IUserRepository userRepository
@foreach(var user in users)
{
<ShowUserInformation user="@item"></ShowUserInformation>
}
@code {
private List<User> users = new();
protected override async Task OnInitializedAsync()
{
Console.WriteLine("I was called");
users = await userRepository.GetAllUsers();
}
}
Se você habilitar a pré-renderização e olhar no console de saída, verá que eu fui chamado duas vezes. A razão é simples. A primeira vez que OnInitializedAsync é chamado acontece no servidor. Portanto, o servidor tem que fazer todo o trabalho para criar o site html estático. Depois que terminarmos e o conteúdo for enviado ao usuário, o segundo OnInitializedAsync entrará em ação. Como agora os circuitos WebAssembly ou SignalR são iniciados, eles farão a mesma coisa novamente. Eles não "sabiam" que já estava renderizado.
Então, se você fizer trabalho pesado, será feito duas vezes! Obviamente um site para baixo. Além disso: seu usuário pode ver uma oscilação óbvia à medida que a página é atualizada.
Há algo que possamos fazer?
.NET 6 para o resgate - conheça persist-component-state
Agora o .NET6 traz um auxiliar exatamente para esse caso: persist-component-state. Simplificado é um cache preenchido pelo servidor e também enviado para o cliente. Então, uma espécie de cache pré-preenchido.
Vamos dar uma olhada em como ele funciona. A configuração é um pouco diferente para o Blazor do lado do cliente e do lado do servidor.
Configuração
Agora vamos dar uma olhada no lado do cliente primeiro. Vá para o seu Pages/_Host.cshtml e adicione o seguinte logo antes do final da sua tag body.
<body>
<component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
<persist-component-state />
</body>
Portanto, o simples < persist-component-state /> é totalmente suficiente. Agora, como vamos usá-lo?
Vamos dar uma olhada no nosso "novo" Index.razor
@page "/"
@implements IDisposable
@inject PersistentComponentState applicationState
@inject IUserRepository userRepository
@foreach(var user in users)
{
<ShowUserInformation user="@item"></ShowUserInformation>
}
@code {
private const string cachingKey = "something_unique";
private List<User> users = new();
private PersistingComponentStateSubscription persistingSubscription;
protected override async Task OnInitializedAsync()
{
persistingSubscription = applicationState.RegisterOnPersisting(PersistData);
if (!applicationState.TryTakeFromJson<List<User>>(cachingKey, out var restored))
{
users = restored;
}
else
{
users = await userRepository.GetAllUsers();
}
}
public void Dispose()
{
persistingSubscription.Dispose();
}
private Task PersistData()
{
applicationState.PersistAsJson(cachingKey, users);
return Task.CompletedTask;
}
}
Agora vamos desembrulhar isso um pouco:
- @inject PersistentComponentState applicationState este é o componente que faz o "cache" para nós.
- Aprimoramos nosso OnInitializedAsync com persistingSubscription = applicationState.RegisterOnPersisting(PersistData);. A ideia é criar uma assinatura para que a gente adicione algo no cache que fique lá do lado do cliente também.
- Se fecharmos nossa página da web / navegarmos em outro lugar, teremos que cancelar a assinatura. Isso é feito na mensagem Dispose. É por isso que adicionamos @implements IDisposable no topo
- A parte seguinte é apenas: Temos sob a chave alguma entrada, se sim, retorne-a e preencha-a nos usuários, caso contrário, vá para o repositório.
if (!applicationState.TryTakeFromJson<List<User>>(cachingKey, out var restored))
{
users = restored;
}
else
{
users = await userRepository.GetAllUsers();
}
Esteja ciente de que é muito importante ter a mesma chave para salvar e restaurar as entradas de cache. Se você já usou o IMemoryCache, está bem ciente.
Recursos
Se você quiser saber mais sobre este assunto, dê uma olhada no seguinte: