Renderizar HTML com JavaScript é diferente de renderizar HTML que é enviado pelo servidor, e que pode afetar o desempenho. Saiba a diferença neste guia e o que você pode fazer para preservar o desempenho de renderização do seu site, especialmente no que diz respeito às interações.
Analisar e renderizar HTML é 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 do aplicativo. Esse certamente é o caso de 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. A renderização do lado do cliente é o nome desse padrão de design e poderá afetar a Interaction to Next Paint (INP) do site se o trabalho envolvido for excessivo.
Este guia o ajudará a pesar a diferença entre usar o HTML enviado pelo servidor ao navegador e criá-lo no cliente com JavaScript, e como este último 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 nos 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 em uma MPA, ocorrerá a seguinte série de eventos:
- O navegador envia uma solicitação de navegação para o URL fornecido.
- O servidor responde com HTML em blocos.
A última etapa é fundamental. Ela também é uma das otimizações de desempenho mais fundamentais na troca de 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 esperar pela chegada de toda a resposta, o navegador poderá processar o HTML em blocos assim que ele chegar.
![Uma captura de tela da análise do HTML enviado pelo servidor, visualizada no painel de desempenho do Chrome DevTools. À medida que o HTML é transmitido, partes dele são processadas em várias tarefas mais curtas, e a renderização é incremental.](https://web.dev/static/articles/client-side-rendering-of-html-and-interactivity/image/a-screenshot-parsing-ht-f2443fe1b1888.png?authuser=5&hl=pt)
Como quase tudo 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 bits desse fluxo chegam em blocos. A consequência é que o navegador rende à linha de execução principal periodicamente após o processamento de cada fragmento, o que evita tarefas longas. Isso significa que outros trabalhos podem ocorrer enquanto o HTML é analisado, incluindo o trabalho de renderização incremental necessário para apresentar uma página ao usuário, bem como o processamento das interações do usuário que podem ocorrer durante o período crucial de inicialização da página. Essa abordagem resulta em uma melhor pontuação 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 é possível fazer isso com a renderização do lado do cliente.
Como o navegador renderiza o HTML fornecido pelo JavaScript
Embora toda solicitação de navegação para uma página exija que uma certa 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 reunido a partir dos dados obtidos do servidor. Navegações subsequentes (às vezes chamadas de "navegações suaves") neste caso, são manipulados inteiramente pelo JavaScript para preencher a página com o novo HTML.
A renderização pelo cliente também pode ocorrer em não SPAs em casos mais limitados, nos quais o HTML é adicionado dinamicamente ao DOM por meio do JavaScript.
Existem algumas formas comuns de criar HTML ou adicionar ao DOM usando JavaScript:
- A propriedade
innerHTML
permite definir o conteúdo em um elemento existente usando uma string, que o navegador analisa no DOM. - O método
document.createElement
permite criar novos elementos a serem adicionados ao DOM sem usar nenhuma análise de HTML no navegador. - O método
document.write
permite que você escreva HTML no documento (e o navegador o analisa, assim como na abordagem no 1). No entanto, devido a vários motivos, o uso dedocument.write
não é recomendado.
![Captura de tela da análise do HTML renderizado pelo JavaScript visualizada no painel de desempenho do Chrome DevTools. O trabalho ocorre em uma única tarefa longa que bloqueia a linha de execução principal.](https://web.dev/static/articles/client-side-rendering-of-html-and-interactivity/image/a-screenshot-parsing-ht-7b89c1ab03511.png?authuser=5&hl=pt)
As consequências da criação de HTML/DOM por meio de JavaScript do lado do cliente podem ser significativas:
- Ao contrário do HTML transmitido pelo servidor em resposta a uma solicitação de navegação, as tarefas JavaScript no cliente não são agrupadas automaticamente, o que pode resultar em tarefas longas que bloqueiam a linha de execução principal. Isso significa que o INP da sua página poderá ser afetado negativamente se você criar muito 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 scanner de pré-carregamento do navegador. Isso certamente terá um efeito negativo na Largest Contentful Paint (LCP) de uma página. Embora esse não seja um problema de desempenho no tempo de execução, mas um problema de atraso de rede na busca de recursos importantes, a LCP do seu site não deve ser afetada por contornar essa otimização fundamental de desempenho do navegador.
O que você pode fazer a respeito do impacto no desempenho da renderização do lado do cliente
Caso seu site dependa muito da renderização do lado do cliente e você tenha observado valores baixos de INP nos dados de campo, talvez esteja se perguntando se a renderização do lado do cliente tem algo a ver com o problema. Por exemplo, se seu site for um SPA, os dados de campo poderão revelar interações responsáveis por um trabalho de renderização considerável.
Seja qual for a causa, confira algumas possíveis causas para colocar tudo de volta nos eixos.
Fornecer o máximo possível de HTML do servidor
Como mencionado anteriormente, por padrão, o navegador lida com HTML do servidor de maneira muito eficiente. Ele divide a análise e a renderização de HTML de forma a evitar tarefas longas e otimiza o tempo total da linha de execução principal. Isso leva a um menor tempo total de bloqueio (TBT, na sigla em inglês), e o TBT está fortemente correlacionado com a INP.
Talvez você dependa de uma estrutura de front-end para criar seu site. Nesse caso, certifique-se de que está renderizando o HTML do componente no servidor. Isso limitará a quantidade de renderização inicial do lado do cliente que seu site exigirá e resultará em uma experiência melhor.
- Para o React, use a API Server DOM para renderizar HTML no servidor. Mas lembre-se: o método tradicional de renderização do lado do servidor usa uma abordagem síncrona, que pode levar a um tempo até o 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 JavaScript para que o servidor possa começar a transmitir HTML para o navegador o mais rápido possível. O Next.js, um framework baseado em React, oferece muitas práticas recomendadas por padrão. Além de renderizar automaticamente o HTML no servidor, ele também pode gerar HTML estaticamente para páginas que não se alteram com base no contexto do usuário (como autenticação).
- A Vue também realiza a renderização do 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 uma abstração de nível superior para seu projeto do Vue a fim de facilitar a implementação das práticas recomendadas.
- O Svelte renderiza o HTML no servidor por padrão. No entanto, se o código do 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 é o Svelte como Next.js e o React, incorpora muitas práticas recomendadas aos projetos do Svelte. Assim, você evita possíveis armadilhas nos projetos que só usam o Svelte.
Limitar a quantidade de nós DOM criados no cliente
Quando os DOMs são grandes, o processamento necessário para renderizá-los tende a aumentar. Não importa se seu site é um SPA completo ou está injetando novos nós em um DOM existente como resultado de uma interação com uma 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 manter o INP do seu site menor.
Considere 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 é a que 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
enquanto a API ReadableStream
é usada para buscar o restante do HTML de uma página no servidor.
Quando esta técnica é usada com sucesso, 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á carregando rapidamente. Sites que usam essa abordagem podem parecer quase um SPA, mas sem as desvantagens da renderização pelo cliente. Isso 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, mas faz um acréscimo a ela. Para mais informações sobre como fazer isso com o Workbox, leia Aplicativos mais rápidos de várias páginas com streams.
Conclusão
A forma como seu site recebe e renderiza HTML afeta o desempenho. Quando você depende do servidor para enviar todo (ou a maior parte) do HTML necessário para o funcionamento do seu site, você recebe muito sem custo financeiro: análise e renderização incrementais e rendimento automático à linha de execução principal para evitar tarefas longas.
A renderização de HTML do lado do cliente apresenta uma série de possíveis problemas de desempenho que podem ser evitados em muitos casos. No entanto, devido aos requisitos de cada site individual, não é possível evitar 100% das vezes. Para reduzir as possíveis tarefas longas que podem resultar do excesso de renderização do site do cliente, certifique-se de que você está enviando o máximo de HTML do seu site do servidor sempre que possível, mantenha os tamanhos do DOM o menor possível para o HTML que precisa ser renderizado no cliente e considere arquiteturas alternativas para acelerar a entrega de HTML ao cliente e, ao mesmo tempo, aproveitar a análise incremental e a renderização que o navegador fornece para HTML carregado do servidor.
Se conseguir a menor renderização possível no lado do cliente, você vai melhorar não apenas o INP do site, mas também outras métricas, como LCP, TBT e possivelmente até mesmo o TTFB, em alguns casos.
Imagem principal do Unsplash, de Maik Jonietz.