Reduzir o escopo e a complexidade dos cálculos de estilo

O JavaScript geralmente é o acionador de mudanças visuais. Às vezes, diretamente por manipulações de estilo e, outras vezes, por cálculos que resultam em mudanças visuais, como pesquisa ou classificação de alguns dados. O JavaScript de longa duração ou no momento errado pode ser uma causa comum de problemas de desempenho, e você deve minimizar esse impacto sempre que possível.

Alterar o DOM, seja por meio da adição e remoção de elementos, alteração de atributos, classes ou animação, fará com que o navegador recalcule os estilos dos elementos e, em muitos casos, o layout (ou reflow) da página ou de partes dela. Esse processo é chamado de cálculo de estilo calculado.

A primeira parte dos estilos de computação é criar um conjunto de seletores correspondentes, que é essencialmente o navegador descobrindo quais classes, pseudosseletores e IDs se aplicam a um determinado elemento.

A segunda parte do processo envolve pegar todas as regras de estilo dos seletores correspondentes e descobrir quais estilos finais o elemento tem.

Resumo

  • Como a redução dos custos de cálculo de estilo pode diminuir a latência de interação.
  • Reduza a complexidade dos seletores. Use uma metodologia centrada na classe (BEM, por exemplo).
  • Reduza o número de elementos em que o cálculo do estilo precisa ser calculado.

Tempo de recálculo do estilo e latência de interação

A Interação com a próxima exibição (INP, na sigla em inglês) é uma métrica de desempenho de tempo de execução centrada no usuário que avalia a capacidade de resposta geral de uma página à entrada do usuário. Quando a latência de interação é avaliada por essa métrica, ela mede o tempo a partir do momento em que o usuário interage com a página até o navegador exibir o próximo frame mostrando as atualizações visuais correspondentes feitas na interface do usuário.

Um componente significativo de uma interação é o tempo que leva para pintar o próximo frame. O trabalho de renderização feito para apresentar o próximo frame é composto de muitas partes, incluindo o cálculo dos estilos de página que ocorrem logo antes do trabalho de layout, pintura e composição. Embora este artigo se concentre apenas nos custos de cálculo de estilo, é importante enfatizar que a redução de qualquer parte da fase de renderização inerente à interação reduzirá a latência total, incluindo o cálculo de estilo.

Reduza a complexidade dos seletores

No caso mais simples, você pode referenciar um elemento no seu CSS com apenas uma classe:

.title {
  /* styles */
}

Mas, à medida que qualquer projeto cresce, ele provavelmente resultará em um CSS mais complexo, e você poderá acabar com seletores parecidos com estes:

.box:nth-last-child(-n+1) .title {
  /* styles */
}

Para saber como esses estilos se aplicam à página, o navegador precisa perguntar "este é um elemento com uma classe de title que tem um elemento pai que é o elemento menos enésimo filho mais 1 com uma classe de box?". A descoberta pode levar muito tempo, dependendo do seletor usado e do navegador em questão. O comportamento pretendido do seletor pode ser alterado para uma classe:

.final-box-title {
  /* styles */
}

Você pode ter problemas com o nome da classe, mas o trabalho acaba de ficar muito mais simples para o navegador. Na versão anterior, para saber, por exemplo, que o elemento é o último de seu tipo, o navegador precisa primeiro saber tudo sobre todos os outros elementos e se há algum elemento que vem depois dele ou seja o nth-last-child, que é potencialmente mais caro do que simplesmente corresponder o seletor ao elemento porque a classe corresponde.

Reduzir o número de elementos sendo estilizados

Outra consideração de desempenho, que normalmente é o fator mais importante para muitas atualizações de estilo, é o grande volume de trabalho que precisa ser realizado quando um elemento muda.

Em termos gerais, o custo do pior caso de cálculo do estilo computado dos elementos é o número de elementos multiplicado pela contagem do seletor, porque cada elemento precisa ser verificado pelo menos uma vez em todos os estilos para conferir se corresponde.

Os cálculos de estilo geralmente podem ser direcionados diretamente a alguns elementos, em vez de invalidar a página como um todo. Em navegadores mais recentes, isso tende a ser menos problemático porque o navegador não precisa necessariamente verificar todos os elementos possivelmente afetados por uma alteração. Por outro lado, navegadores mais antigos não são necessariamente otimizados para essas tarefas. Sempre que possível, reduza o número de elementos invalidados.

Medir o custo de recálculo do estilo

Uma forma de medir o custo dos recálculos de estilo é usar o painel "Performance" no Chrome DevTools. Para começar, abra o DevTools, acesse a guia Performance, clique em "Gravar" e interaja com a página. Quando a gravação for interrompida, você verá algo parecido com a imagem abaixo:

DevTools mostrando cálculos de estilo.

A faixa no topo é um diagrama de chamas em miniatura que também representa quadros por segundo. Quanto mais próxima a atividade estiver da parte inferior da faixa, mais rápidos os frames serão pintados pelo navegador. Se você vir o diagrama de chamas nivelando na parte superior com faixas vermelhas acima dele, isso significa que o trabalho está causando frames de longa duração.

Aumentar o zoom de uma área problemática no Chrome DevTools no resumo da atividade do painel de desempenho preenchido no Chrome DevTools.

Se você tiver um frame de longa duração durante uma interação como a rolagem, ele precisará passar por uma análise mais detalhada. Se você tiver um bloco roxo grande, aumente o zoom na atividade e selecione o trabalho chamado Recalcular estilo para ter mais informações sobre o trabalho de recálculo de estilo que pode ser caro.

Receber detalhes de cálculos de estilo de longa duração, incluindo informações vitais, como a quantidade de elementos afetados pelo trabalho de recálculo de estilo.

Nessa captura, há um trabalho de recálculo de estilo de longa duração que leva pouco mais de 25ms.

Se você clicar no evento, receberá uma pilha de chamadas. Se o trabalho de renderização ocorreu devido a uma interação do usuário, o local no JavaScript que é responsável por acionar a alteração de estilo será destacado. Além disso, você também recebe o número de elementos que foram afetados pela mudança (pouco mais de 900 elementos neste caso) e quanto tempo levou para fazer o cálculo do estilo. Use essas informações para tentar encontrar uma correção no código.

Usar bloco, elemento, modificador

Abordagens para codificação como BEM (bloco, elemento, modificador), na verdade, incorporam os benefícios de desempenho de correspondência do seletor acima, porque recomenda que tudo tenha uma única classe e, em que você precisa de hierarquia, também esteja incorporada no nome da classe:

.list {
  /* Styles */
}

.list__list-item {
  /* Styles */
}

Se você precisar de algum modificador, como no exemplo acima, em que queremos fazer algo especial para o último filho, adicione-o assim:

.list__list-item--last-child {
  /* Styles */
}

Se você está procurando uma boa maneira de organizar seu CSS, o BEM é um bom ponto de partida, tanto do ponto de vista da estrutura quanto devido às simplificações de pesquisa de estilo que a metodologia promove.

Se você não gosta do BEM, há outras maneiras de abordar o CSS, mas as considerações de desempenho devem ser avaliadas junto com a ergonomia da abordagem.

Recursos

Imagem principal do Unsplash, por Markus Spiske.