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

O JavaScript normalmente é um acionador de mudanças visuais. Às vezes, elas são feitas diretamente por manipulações de estilo e, às vezes, por cálculos que resultam em mudanças visuais, como pesquisa ou classificação de dados. O JavaScript de longa duração ou no momento errado pode ser uma causa comum de problemas de desempenho. Por isso, minimize o impacto dele sempre que possível.

Cálculo do estilo

Mudar o DOM adicionando e removendo elementos, mudando atributos e classes ou reproduzindo animações faz com que o navegador recalcule os estilos dos elementos e, em muitos casos, o layout de parte ou de toda a página. Esse processo é chamado de cálculo de estilo calculado.

O navegador começa a calcular estilos criando um conjunto de seletores correspondentes para determinar quais classes, pseudosseletores e IDs se aplicam a um determinado elemento. Em seguida, ela processa as regras de estilo dos seletores correspondentes e descobre quais estilos finais o elemento tem.

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 execução centrada no usuário que avalia a capacidade de resposta geral de uma página à entrada do usuário. Mede a latência de interação desde o 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 na interface do usuário.

Um componente significativo de uma interação é o tempo necessário para pintar o próximo frame. O trabalho de renderização feito para apresentar o próximo frame é composto de várias partes, incluindo o cálculo dos estilos de página que ocorrem pouco antes dos trabalhos de layout, pintura e composição. O foco desta página são os custos de cálculo de estilo. No entanto, a redução de qualquer parte da fase de renderização relacionada à interação também reduz a latência total dela, inclusive para cálculos de estilo.

Reduza a complexidade dos seletores

Simplificar os nomes dos seletores pode acelerar os cálculos de estilo da sua página. Os seletores mais simples fazem referência a um elemento no CSS apenas com um nome de classe:

.title {
  /* styles */
}

No entanto, à medida que qualquer projeto cresce, ele provavelmente precisa de um CSS mais complexo, e é possível que você fique com seletores como estes:

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

Para determinar como esses estilos se aplicam à página, o navegador precisa perguntar "este é um elemento com uma classe de title cujo pai é o elemento filho menos-enésimo-mais-1 com uma classe de box". A descoberta pode levar muito tempo, dependendo do seletor usado e do navegador em questão. Para simplificar, altere o seletor para ser apenas um nome de classe:

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

Esses nomes de classe de substituição podem parecer estranhos, mas tornam o trabalho do navegador muito mais simples. Na versão anterior, por exemplo, para que o navegador saiba que um elemento é o último desse tipo, ele precisa primeiro saber tudo sobre todos os outros elementos para determinar se algum elemento que vem depois dele pode ser o nth-last-child. Em termos computacionais, isso pode ser muito mais caro do que corresponder um seletor a um elemento só porque a classe dele corresponde.

Reduzir o número de elementos sendo estilizados

Outra consideração de desempenho, e muitas vezes mais importante que a complexidade do seletor, é a quantidade de trabalho que precisa acontecer quando um elemento muda.

Em termos gerais, o custo do pior caso de cálculo do estilo de elementos computados é o número de elementos multiplicado pela contagem de seletores, porque o navegador precisa comparar cada elemento pelo menos uma vez em todos os estilos para conferir se ele corresponde.

Os cálculos de estilo podem ter como alvo alguns elementos diretamente, em vez de invalidar a página inteira. Em navegadores mais recentes, isso tende a ser um problema menor, porque o navegador nem sempre precisa verificar todos os elementos que uma mudança pode afetar. Por outro lado, navegadores mais antigos nem sempre são otimizados para essas tarefas. Sempre que possível, reduza o número de elementos invalidados.

Medir o custo de recálculo do estilo

Uma maneira de medir o custo dos recálculos de estilo é usar o painel de desempenho no Chrome DevTools. Para começar, faça o seguinte:

  1. Abra o DevTools.
  2. Acesse a guia Desempenho.
  3. Clique em Gravar.
  4. Interaja com a página.

Quando a gravação for interrompida, você verá algo parecido com a seguinte imagem:

DevTools mostrando cálculos de estilo.
Um relatório do DevTools mostrando cálculos de estilo.

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

Aumentar o zoom em
    uma área problemática no Chrome DevTools no resumo da atividade do painel
    de desempenho preenchido no Chrome DevTools.
Frames de longa duração no resumo de atividades do DevTools.

Frames de longa duração durante uma interação, como a rolagem, valem uma análise mais detalhada. Se você encontrar um grande bloco roxo, aumente o zoom na atividade e selecione um trabalho chamado ReCalcular estilo para ter mais informações sobre o trabalho de recálculo de estilos potencialmente 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.
Um recálculo de estilo de longa duração que leva pouco mais de 25&nbspms no resumo do DevTools.

Clicar no evento mostra a pilha de chamadas. Se o trabalho de renderização foi causado por uma interação do usuário, ele chama o JavaScript que acionou a mudança de estilo. Ele também mostra o número de elementos que a mudança afeta (pouco mais de 900 elementos nesse caso) e quanto tempo levou o cálculo do estilo. Use essas informações para tentar encontrar uma correção no código.

Usar bloco, elemento, modificador

Abordagens para a programação como BEM (bloqueio, elemento, modificador) incluem os benefícios de desempenho de correspondência do seletor. O BEM recomenda que tudo tenha uma única classe e, quando necessário, essa hierarquia também será incorporada ao nome da classe:

.list {
  /* Styles */
}

.list__list-item {
  /* Styles */
}

Se você precisar de um modificador, como no exemplo do último filho, poderá adicioná-lo desta forma:

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

O BEM é um bom ponto de partida para organizar seu CSS, tanto do ponto de vista da estrutura quanto devido às simplificações de pesquisa de estilo que ele promove.

Se você não gosta do BEM, há outras maneiras de abordar o CSS, mas avalie o desempenho e a ergonomia dele antes de começar.

Recursos

Imagem principal do Unsplash, por Markus Spiske.