Renderizar HTML com JavaScript é diferente de renderizar HTML enviado pelo servidor e que pode afetar o desempenho. Conheça a diferença neste guia e saiba o que fazer para preservar o desempenho de renderização do seu site, principalmente no que diz respeito às interações.
A análise e a renderização de HTML são algo que os navegadores fazem muito bem por padrão para sites que usam a lógica de navegação integrada do navegador, às vezes chamada de "carregamentos de página tradicionais" ou "navegações difíceis". Esses sites às vezes são chamados de aplicativos de várias páginas (MPAs, na sigla em inglês).
No entanto, os desenvolvedores podem contornar os padrões do navegador para atender às necessidades de seus aplicativos. Esse com certeza é o caso dos sites que usam o padrão de aplicativo de página única (SPA, na sigla em inglês), que cria dinamicamente grandes partes do HTML/DOM no cliente com JavaScript. "Renderização do lado do cliente" é o nome desse padrão de design e poderá afetar a Interação com a próxima exibição (INP, na sigla em inglês) do seu site se o trabalho envolvido for excessivo.
Este guia vai ajudá-lo a saber a diferença entre usar HTML enviado pelo servidor ao navegador e criá-lo no cliente com JavaScript, e como o segundo pode resultar em alta latência de interação em momentos cruciais.
Como o navegador renderiza o HTML fornecido pelo servidor
O padrão de navegação usado em carregamentos de página tradicionais envolve o recebimento de HTML do servidor em cada navegação. Se você inserir um URL na barra de endereço do seu navegador ou clicar em um link de um MPA, a seguinte série de eventos vai ocorrer:
- O navegador envia uma solicitação de navegação para o URL fornecido.
- O servidor responde com HTML em partes.
A última etapa delas é fundamental. Essa também é uma das otimizações de desempenho mais fundamentais na troca entre servidor/navegador e é conhecida como streaming. Se o servidor puder começar a enviar HTML o mais rápido possível e o navegador não aguardar a chegada inteira da resposta, o navegador poderá processar o HTML em partes, assim que ele chegar.
Como a maioria das coisas que acontece no navegador, a análise de HTML ocorre nas tarefas. Quando o HTML é transmitido do servidor para o navegador, o navegador otimiza a análise desse HTML, fazendo isso aos poucos, à medida que os pedaços do fluxo chegam em partes. A consequência é que o navegador acessa a linha de execução principal periodicamente depois de processar cada bloco, o que evita tarefas longas. Isso significa que outros trabalhos podem ocorrer enquanto o HTML está sendo analisado, incluindo o trabalho de renderização incremental necessário para apresentar uma página ao usuário, bem como o processamento de interações do usuário que podem ocorrer durante o período crucial de inicialização da página. Essa abordagem se traduz em uma pontuação melhor de Interaction to Next Paint (INP) para a página.
Que lição podemos tirar disso? Ao fazer streaming de HTML do servidor, você recebe análise e renderização incrementais de HTML e rendimento automático para a linha de execução principal sem custo financeiro. Não há isso com a renderização do lado do cliente.
Como o navegador renderiza HTML fornecido pelo JavaScript
Embora cada solicitação de navegação para uma página exija que uma quantidade de HTML seja fornecida pelo servidor, alguns sites usam o padrão SPA. Essa abordagem geralmente envolve uma carga inicial mínima de HTML fornecida pelo servidor, mas depois o cliente preenche a área de conteúdo principal de uma página com HTML montado a partir de dados obtidos do servidor. As navegações subsequentes, neste caso, às vezes chamadas de "navegações flexíveis", são inteiramente controladas por JavaScript para preencher a página com o novo HTML.
A renderização do lado do cliente também pode ocorrer em não-SPAs em casos mais limitados, nos quais o HTML é dinamicamente adicionado ao DOM por meio de JavaScript.
Existem algumas maneiras comuns de criar HTML ou adicionar ao DOM por meio de JavaScript:
- A propriedade
innerHTML
permite definir o conteúdo em um elemento existente usando uma string, que o navegador analisa em DOM. - Com o método
document.createElement
, você pode criar novos elementos a serem adicionados ao DOM sem usar a análise de HTML do navegador. - O método
document.write
permite que você grave HTML no documento e o navegador o analise, como na abordagem 1. No entanto, devido a vários motivos, o uso dedocument.write
não é recomendado.
As consequências da criação de HTML/DOM por meio do JavaScript do lado do cliente podem ser significativas:
- Diferentemente do HTML transmitido pelo servidor em resposta a uma solicitação de navegação, as tarefas JavaScript no cliente não são divididas automaticamente, o que pode resultar em tarefas longas que bloqueiam o thread principal. Isso significa que o INP da sua página poderá ser afetado negativamente se você criar muitos HTML/DOM de uma só vez no cliente.
- Se o HTML for criado no cliente durante a inicialização, os recursos referenciados nele não serão descobertos pelo verificador de pré-carregamento do navegador. Isso certamente terá um efeito negativo na Maior exibição de conteúdo (LCP) de uma página. Embora isso não seja um problema de desempenho no ambiente de execução (em vez de um problema de atraso da rede na busca de recursos importantes), você não quer que a LCP do seu site seja afetada por ignorar essa otimização fundamental do desempenho do navegador.
O que você pode fazer a respeito do impacto da performance da renderização do lado do cliente
Caso seu site dependa muito da renderização do lado do cliente e você tenha observado valores de INP ruins nos dados de campo, talvez se pergunte se a renderização do lado do cliente tem algo a ver com o problema. Por exemplo, se o site for um SPA, os dados de campo poderão revelar interações responsáveis por um trabalho considerável de renderização.
Seja qual for o motivo, aqui estão algumas possíveis causas que você pode explorar para ajudar a colocar tudo de volta nos trilhos.
Forneça o máximo de HTML possível do servidor
Como mencionado anteriormente, o navegador lida com HTML do servidor por padrão de maneira muito eficiente. Ele divide a análise e a renderização de HTML de modo a evitar tarefas longas e otimizar o tempo total da linha de execução principal. Isso leva a um Tempo total de bloqueio (TBT, na sigla em inglês) menor, e o TBT tem alta correlação com o INP.
Talvez você precise de uma estrutura de front-end para criar o site. Nesse caso, verifique se você está renderizando o HTML do componente no servidor. Isso limitará a quantidade de renderização inicial do lado do cliente exigida pelo site e resultará em uma experiência melhor.
- No React, convém usar a Server DOM API para renderizar HTML no servidor. Mas lembre-se: o método tradicional de renderização do lado do servidor usa uma abordagem síncrona, o que pode levar a um tempo para primeiro byte (TTFB, na sigla em inglês) mais longo, bem como métricas subsequentes, como First Contentful Paint (FCP) e LCP. Sempre que possível, use as APIs de streaming para Node.js ou outros ambientes de execução de JavaScript. Assim, o servidor poderá começar a transmitir HTML para o navegador o mais rápido possível. Por padrão, o Next.js, um framework baseado em React, oferece muitas práticas recomendadas. Além de renderizar o HTML automaticamente no servidor, ele também pode gerar HTML estaticamente para páginas que não mudam com base no contexto do usuário (como autenticação).
- O Vue também executa a renderização no lado do cliente por padrão. No entanto, assim como o React, o Vue também pode renderizar o HTML do componente no servidor. Use essas APIs do lado do servidor sempre que possível ou considere usar uma abstração de nível superior no seu projeto do Vue para facilitar a implementação das práticas recomendadas.
- O Svelte renderiza o HTML no servidor por padrão, mas se o código do seu componente precisar de acesso a namespaces exclusivos do navegador (
window
, por exemplo), talvez não seja possível renderizar o HTML desse componente no servidor. Explore abordagens alternativas sempre que possível para não causar renderização desnecessária no lado do cliente. O SvelteKit, que é para o Svelte como o Next.js, incorpora várias práticas recomendadas nos projetos do Svelte. Assim, você pode evitar possíveis armadilhas em projetos que usam apenas o Svelte.
Limitar a quantidade de nós DOM criados no cliente
Quando os DOMs são grandes, o tempo de processamento necessário para renderizá-los tende a aumentar. Não importa se o site é um SPA completo ou está injetando novos nós em um DOM já existente como resultado de uma interação com um MPA, considere manter esses DOMs os menores possíveis. Isso ajudará a reduzir o trabalho necessário durante a renderização no lado do cliente para exibir esse HTML, ajudando a diminuir o INP do seu site.
Considerar uma arquitetura de service worker de streaming
Essa é uma técnica avançada, que pode não funcionar facilmente em todos os casos de uso, mas pode transformar sua MPA em um site que parece estar carregando instantaneamente quando os usuários navegam de uma página para outra. É possível usar um service worker para pré-armazenar em cache as partes estáticas do seu site no CacheStorage
e usar a API ReadableStream
para buscar o restante do HTML de uma página no servidor.
Ao usar essa técnica de forma bem-sucedida, você não está criando HTML no cliente, mas o carregamento instantâneo de partes parciais do conteúdo do cache dará a impressão de que o site está sendo carregado rapidamente. Sites que usam essa abordagem podem parecer quase um SPA, mas sem as desvantagens da renderização do lado do cliente. Também reduz a quantidade de HTML que você está solicitando ao servidor.
Em resumo, uma arquitetura de service worker de streaming não substitui a lógica de navegação integrada do navegador, ela adiciona a ela. Para mais informações sobre como fazer isso com o Workbox, leia Aplicativos de várias páginas mais rápidos com streams.
Conclusão
A forma como seu site recebe e renderiza HTML tem um impacto no desempenho. Quando depende do servidor para enviar todo (ou a maior parte) o HTML necessário para o funcionamento do seu site, você recebe muitos benefícios sem custo financeiro: análise e renderização incrementais e rendimento automático para a linha de execução principal para evitar tarefas longas.
A renderização HTML do lado do cliente apresenta uma série de possíveis problemas de desempenho que podem ser evitáveis em muitos casos. No entanto, devido aos requisitos de cada site, isso não pode ser evitado 100% das vezes. Para reduzir as possíveis tarefas longas que podem resultar do excesso de renderização no site do cliente, certifique-se de enviar o máximo possível do HTML do seu site a partir do servidor sempre que possível, mantenha o tamanho do DOM o menor possível para HTML que deve ser renderizado no cliente e considere arquiteturas alternativas para acelerar a entrega de HTML ao cliente enquanto também aproveita a análise incremental e a renderização que o navegador oferece para o HTML carregado pelo servidor.
Se conseguir que a renderização do lado do cliente do seu site seja mínima, você vai melhorar não apenas o INP do site, mas também outras métricas, como LCP, TBT e talvez até seu TTFB em alguns casos.
Imagem principal do Unsplash, por Maik Jonietz.