Como tamanhos grandes de DOM afetam a interatividade e o que você pode fazer a respeito

Grandes tamanhos de DOM têm mais efeito na interatividade do que você imagina. Este guia explica o motivo e o que você pode fazer.

Não há como evitar: quando você cria uma página da Web, ela tem um Modelo de objeto de documentos (DOM, na sigla em inglês). O DOM representa a estrutura do HTML da página e permite que JavaScript e CSS acessem a estrutura e o conteúdo da página.

No entanto, o problema é que o tamanho do DOM afeta a capacidade do navegador de renderizar uma página de maneira rápida e eficiente. De modo geral, quanto maior é um DOM, mais caro é para renderizar inicialmente essa página e atualizar a renderização mais tarde no ciclo de vida da página.

Isso se torna problemático em páginas com DOMs muito grandes quando interações que modificam ou atualizam o DOM acionam trabalhos de layout caros que afetam a capacidade da página de responder rapidamente. Um trabalho de layout pesado pode afetar a Interação com a próxima exibição (INP, na sigla em inglês) de uma página. Se você quiser que uma página responda rapidamente às interações do usuário, é importante garantir que os tamanhos do DOM sejam tão grandes quanto necessário.

Quando o DOM de uma página é muito grande?

De acordo com o Lighthouse, o tamanho do DOM de uma página é excessivo quando ultrapassa 1.400 nós. O Lighthouse começará a exibir avisos quando o DOM de uma página exceder 800 nós. Veja o seguinte HTML, por exemplo:

<ul>
  <li>List item one.</li>
  <li>List item two.</li>
  <li>List item three.</li>
</ul>

No código acima, há quatro elementos DOM: <ul> e os três filhos <li>. Sua página da Web certamente terá muito mais nós do que esse, por isso é importante entender o que você pode fazer para manter os tamanhos do DOM sob controle, além de outras estratégias para otimizar o trabalho de renderização quando o DOM de uma página for tão pequeno.

Como os DOMs grandes afetam o desempenho da página?

DOMs grandes afetam o desempenho da página de algumas maneiras:

  1. Durante a renderização inicial da página. Quando o CSS é aplicado a uma página, uma estrutura semelhante ao DOM, conhecida como modelo de objeto CSS (CSSOM, na sigla em inglês), é criada. À medida que os seletores CSS aumentam em especificidade, o CSSOM fica mais complexo, e é necessário mais tempo para executar o trabalho necessário de layout, estilo, composição e pintura para desenhar a página da Web na tela. Esse trabalho adicional aumenta a latência das interações que ocorrem no início do carregamento da página.
  2. Quando as interações modificam o DOM, seja por meio da inserção ou exclusão de elementos, ou pela modificação de conteúdos e estilos do DOM, o trabalho necessário para renderizar essa atualização pode resultar em trabalhos de layout, estilo, composição e pintura muito caros. Como acontece com a renderização inicial da página, um aumento na especificidade do seletor de CSS pode aumentar o trabalho de renderização quando elementos HTML são inseridos no DOM como resultado de uma interação.
  3. Quando JavaScript consulta o DOM, as referências aos elementos do DOM são armazenadas na memória. Por exemplo, se você chamar document.querySelectorAll para selecionar todos os elementos <div> de uma página, o custo de memória poderá ser considerável se o resultado retornar um grande número de elementos DOM.
Captura de tela de uma tarefa longa causada por trabalho excessivo de renderização no painel de desempenho do Chrome DevTools. A pilha de chamadas da tarefa longa mostra um tempo significativo gasto no cálculo de estilos de páginas e na pré-pintura.
Uma tarefa longa, como mostrado no Performance Profiler no Chrome DevTools. A tarefa longa mostrada é causada pela inserção de elementos DOM em um DOM grande via JavaScript.

Tudo isso pode afetar a interatividade, mas o segundo item da lista acima é especialmente importante. Se uma interação resultar em uma mudança no DOM, ela poderá iniciar uma grande quantidade de trabalho que poderá contribuir para um INP ruim em uma página.

Como faço para medir o tamanho do DOM?

Você pode medir o tamanho do DOM de algumas maneiras. O primeiro método usa o Lighthouse. Quando você executa uma auditoria, as estatísticas sobre o DOM da página atual aparecem na auditoria "Evitar um tamanho excessivo do DOM" no título "Diagnóstico". Nesta seção, você pode conferir o número total de elementos DOM, o elemento DOM que contém mais elementos filhos e o elemento DOM mais profundo.

Um método mais simples envolve o uso do console JavaScript nas ferramentas para desenvolvedores dos principais navegadores. Para ver o número total de elementos HTML no DOM, use o seguinte código no console após o carregamento da página:

document.querySelectorAll('*').length;

Para ver a atualização do tamanho do DOM em tempo real, use a ferramenta de monitoramento de desempenho. Com essa ferramenta, você pode correlacionar operações de layout e estilo (e outros aspectos de desempenho) com o tamanho atual do DOM.

Uma captura de tela do monitor de desempenho no Chrome DevTools. À esquerda, há vários aspectos do desempenho da página que podem ser monitorados continuamente durante a vida útil da página. Na captura de tela, o número de nós do DOM, os layouts por segundo e os recálculos de estilo por seção estão sendo monitorados ativamente.
O monitor de desempenho no Chrome DevTools. Nessa visualização, o número atual de nós DOM da página é representado no gráfico junto com as operações de layout e os recálculos de estilo realizados por segundo.

Se o tamanho do DOM se aproximar do limite de aviso do DOM do Lighthouse ou falhar completamente, a próxima etapa será descobrir como reduzir o tamanho do DOM para melhorar a capacidade da página de responder às interações do usuário e melhorar o INP do site.

Como posso medir o número de elementos DOM afetados por uma interação?

Se você estiver criando um perfil de uma interação lenta no laboratório e suspeitar que possa ter algo a ver com o tamanho do DOM da página, será possível descobrir quantos elementos DOM foram afetados selecionando qualquer parte da atividade no criador de perfil denominada "ReCalcular estilo" e observar os dados contextuais no painel inferior.

Captura de tela da atividade de recálculo de estilo selecionada no painel de desempenho do Chrome DevTools. Na parte de cima, a faixa de interações mostra uma interação de clique, e a maior parte do trabalho é gasta fazendo o recálculo de estilo e o trabalho de pré-pintura. Na parte inferior, um painel mostra mais detalhes sobre a atividade selecionada, informando que 2.547 elementos DOM foram afetados.
Observar o número de elementos afetados no DOM como resultado do trabalho de recálculo de estilo. A parte sombreada da interação na faixa de interações representa a duração da interação que foi superior a 200 milissegundos, que é o limite "bom" designado para INP.

Na captura de tela acima, observe que o recálculo de estilo do trabalho, quando selecionado, mostra o número de elementos afetados. Embora a captura de tela acima mostre um caso extremo do efeito do tamanho do DOM no trabalho de renderização em uma página com muitos elementos DOM, essas informações de diagnóstico são úteis em qualquer caso para determinar se o tamanho do DOM é um fator limitante do tempo necessário para o próximo frame ser pintado em resposta a uma interação.

Como posso reduzir o tamanho do DOM?

Além de auditar o HTML do seu site para marcações desnecessárias, a principal forma de reduzir o tamanho do DOM é reduzir a profundidade do DOM. Um sinal de que o DOM pode ser desnecessariamente profundo é se você está vendo uma marcação parecida com esta na guia Elementos das ferramentas para desenvolvedores do seu navegador:

<div>
  <div>
    <div>
      <div>
        <!-- Contents -->
      </div>
    </div>
  </div>
</div>

Ao ver padrões como esse, você provavelmente pode simplificá-los nivelando sua estrutura DOM. Isso reduz o número de elementos DOM e provavelmente permite simplificar os estilos de página.

A profundidade do DOM também pode ser um sintoma das estruturas que você usa. Particularmente, os frameworks com base em componentes, como aqueles que dependem do JSX, exigem o aninhamento de vários componentes em um contêiner pai.

No entanto, muitos frameworks permitem evitar o aninhamento de componentes usando o que é conhecido como fragmentos. Os frameworks baseados em componentes que oferecem fragmentos como um recurso incluem, entre outros:

Ao usar fragmentos no framework de sua escolha, é possível reduzir a profundidade do DOM. Se você estiver preocupado com o impacto que o nivelamento da estrutura DOM tem no estilo, pode se beneficiar do uso de modos de layout mais modernos (e mais rápidos), como flexbox ou grid.

Outras estratégias a serem consideradas

Mesmo que você tenha muito cuidado para nivelar a árvore do DOM e remover elementos HTML desnecessários para manter o DOM o menor possível, ele ainda poderá ser muito grande e iniciar muito trabalho de renderização à medida que muda em resposta às interações do usuário. Se você estiver nessa posição, existem algumas outras estratégias que podem ser consideradas para limitar o trabalho de renderização.

Considere uma abordagem aditiva

Talvez você esteja em uma posição em que grandes partes da página não estão visíveis inicialmente para o usuário quando ela é renderizada pela primeira vez. Essa pode ser uma oportunidade para o carregamento lento do HTML, omitindo essas partes do DOM na inicialização, mas as adicione quando o usuário interagir com as partes da página que exigem os aspectos inicialmente ocultos.

Essa abordagem é útil durante o carregamento inicial e talvez até mesmo depois. Para o carregamento inicial da página, você está assumindo menos trabalho de renderização adiantado, o que significa que sua carga HTML inicial será mais leve e será renderizada mais rapidamente. Assim, as interações durante esse período crucial terão mais oportunidades de serem executadas, com menos concorrência pela atenção da linha de execução principal.

Se você tiver muitas partes da página que inicialmente ficam ocultas no carregamento, isso também pode acelerar outras interações que acionam o trabalho de nova renderização. No entanto, à medida que outras interações adicionam mais ao DOM, o trabalho de renderização aumentará à medida que o DOM crescer ao longo do ciclo de vida da página.

Adicionar ao DOM ao longo do tempo pode ser complicado, e tem suas próprias vantagens. Se você seguir esse caminho, provavelmente estará fazendo solicitações de rede para obter dados para preencher o HTML que pretende adicionar à página em resposta a uma interação do usuário. Embora as solicitações de rede em andamento não sejam contabilizadas no INP, isso pode aumentar a latência percebida. Se possível, mostre um ícone de carregamento ou outro indicador de que os dados estão sendo buscados para que os usuários entendam que algo está acontecendo.

Limitar a complexidade do seletor de CSS

Quando o navegador analisa seletores no CSS, ele precisa percorrer a árvore do DOM para entender como e se esses seletores se aplicam ao layout atual. Quanto mais complexos forem esses seletores, mais trabalho o navegador terá que realizar para realizar a renderização inicial da página e os novos recálculos de estilo e o trabalho de layout caso a página seja alterada como resultado de uma interação.

Usar a propriedade content-visibility

O CSS oferece a propriedade content-visibility, que é uma maneira de renderizar lentamente elementos DOM fora da tela. À medida que os elementos se aproximam da janela de visualização, eles são renderizados sob demanda. Os benefícios da content-visibility não apenas cortam uma quantidade significativa de trabalho na renderização inicial da página, mas também pulam o trabalho de renderização para elementos fora da tela quando o DOM da página é modificado como resultado de uma interação do usuário.

Conclusão

Reduzir o tamanho do DOM apenas ao que é estritamente necessário é uma boa maneira de otimizar o INP do seu site. Ao fazer isso, você pode reduzir o tempo que leva para o navegador executar o trabalho de layout e renderização quando o DOM é atualizado. Mesmo que não seja possível reduzir significativamente o tamanho do DOM, existem algumas técnicas que podem ser usadas para isolar o trabalho de renderização em uma subárvore do DOM, como a contenção de CSS e a propriedade CSS content-visibility.

Independentemente do método, você cria um ambiente em que o trabalho de renderização é minimizado e reduz a quantidade de trabalho de renderização que sua página realiza em resposta às interações. Como resultado, seu site ficará mais responsivo quando os usuários interagirem com eles. Isso significa que o seu site terá um INP mais baixo, o que melhora a experiência do usuário.

Imagem principal do Unsplash, de Louis Reed.