Evite layouts grandes e complexos e a troca frequente de layouts

O layout é onde o navegador descobre informações geométricas dos elementos, como tamanho e localização na página. Cada elemento terá informações de tamanho explícitas ou implícitas de acordo com o CSS usado, o conteúdo do elemento ou um elemento pai. O processo é chamado de Layout no Chrome.

O layout é onde o navegador descobre informações geométricas dos elementos: seu tamanho e localização na página. Cada elemento terá informações de tamanho explícitas ou implícitas de acordo com o CSS usado, o conteúdo do elemento ou um elemento pai. O processo é chamado de "Layout no Chrome" (e navegadores derivados, como o Edge) e o Safari. No Firefox, o processo é chamado de Reflow, mas na realidade é o mesmo.

Assim como nos cálculos de estilo, as preocupações imediatas para o custo do layout são:

  1. O número de elementos que exigem layout, que é um subproduto do tamanho do DOM da página.
  2. A complexidade desses layouts.

Resumo

  • O layout tem um efeito direto na latência de interação
  • Normalmente, o escopo do layout é todo o documento.
  • O número de elementos DOM afetará o desempenho; você deve evitar acionar o layout sempre que possível.
  • Evite layouts síncronos forçados e a troca frequente de layouts. Leia os valores de estilo e faça as mudanças necessárias.

Os efeitos do layout na latência de interação

Quando um usuário interage com a página, essas interações devem ser as mais rápidas possível. O tempo que uma interação leva para ser concluída, ou seja, termina quando o navegador apresenta o próximo frame para mostrar os resultados da interação, é conhecido como latência de interação. Esse é um aspecto do desempenho da página que a métrica Interação com a próxima exibição mede.

O tempo que o navegador leva para apresentar o próximo frame em resposta a uma interação do usuário é conhecido como atraso da apresentação da interação. O objetivo de uma interação é fornecer feedback visual para sinalizar ao usuário que algo ocorreu, e atualizações visuais podem envolver uma certa quantidade de trabalho de layout para atingir esse objetivo.

Para manter o INP do seu site o mais baixo possível, é importante evitar o layout quando possível. Se não for possível evitar totalmente o layout, é importante limitar esse trabalho para que o navegador possa apresentar o próximo frame rapidamente.

Evite o layout sempre que possível.

Quando você muda os estilos, o navegador verifica se alguma mudança exige que o layout seja calculado e que a árvore de renderização seja atualizada. As mudanças nas "propriedades geométricas", como larguras, alturas, esquerda ou parte superior, exigem layout.

.box {
  width: 20px;
  height: 20px;
}

/**
  * Changing width and height
  * triggers layout.
  */

.box--expanded {
  width: 200px;
  height: 350px;
}

Quase sempre, o escopo do layout é o documento inteiro. Se você tiver muitos elementos, vai levar muito tempo para descobrir os locais e as dimensões de todos.

Se não for possível evitar o layout, o segredo é usar o Chrome DevTools mais uma vez para ver quanto tempo está demorando e determinar se o layout é a causa de um gargalo. Primeiro, abra o DevTools, acesse a guia "Linha do tempo", clique em "Gravar" e interaja com o site. Ao interromper a gravação, você verá um detalhamento do desempenho do site:

DevTools mostrando muito tempo no Layout.

Ao analisar o rastro no exemplo acima, vemos que mais de 28 milissegundos são gastos dentro do layout para cada frame. Quando temos 16 milissegundos para exibir um frame na tela em uma animação, isso é muito alto. O DevTools também informa o tamanho da árvore (neste caso,1.618 elementos) e quantos nós precisavam de layout (5, neste caso).

Lembre-se de que a recomendação geral aqui é evitar o layout sempre que possível, mas nem sempre é possível evitá-lo. Nos casos em que não é possível evitar o layout, o custo dele tem uma relação com o tamanho do DOM. Embora a relação entre os dois não seja estritamente acoplada, DOMs maiores geralmente terão custos de layout mais altos.

Evitar layouts síncronos forçados

O envio de um frame para a tela é feito nesta ordem:

Usar flexbox como layout.

Primeiro, o JavaScript é executado, depois os cálculos de estilo e depois o layout. No entanto, é possível forçar o navegador a executar antecipadamente o layout com JavaScript. Isso é chamado de layout síncrono forçado.

A primeira coisa a ter em mente é que, à medida que o JavaScript executa, todos os valores de layout antigos do frame anterior são conhecidos e estão disponíveis para consulta. Por exemplo, se você quiser escrever a altura de um elemento (vamos chamá-lo de "caixa") no início do frame, escreva um código assim:

// Schedule our function to run at the start of the frame:
requestAnimationFrame(logBoxHeight);

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

Os problemas ficam mais complicados se você altera os estilos da caixa antes de solicitar a altura:

function logBoxHeight () {
  box.classList.add('super-big');

  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);
}

Agora, para responder à pergunta sobre altura, o navegador precisa primeiro aplicar a mudança de estilo (por causa da adição da classe super-big) e depois executar o layout. Só assim será possível retornar a altura correta. Esse é um trabalho desnecessário e potencialmente caro.

Por isso, você deve sempre agrupar suas leituras de estilo e fazê-las primeiro (onde o navegador pode usar os valores de layout do frame anterior) e depois fazer as gravações:

Concluída corretamente, a função acima seria:

function logBoxHeight () {
  // Gets the height of the box in pixels and logs it out:
  console.log(box.offsetHeight);

  box.classList.add('super-big');
}

Na maioria das vezes, você não precisa aplicar estilos e consultar valores. O uso dos valores do último frame é suficiente. A execução dos cálculos de estilo e do layout de forma síncrona e antes do navegador gostaria de gerar possíveis gargalos, e não algo que você normalmente queira fazer.

Evitar a troca frequente de layouts

Existe uma maneira de piorar ainda mais os layouts síncronos forçados: faça muitos deles em rápida sucessão. Confira este código:

function resizeAllParagraphsToMatchBlockWidth () {
  // Puts the browser into a read-write-read-write cycle.
  for (let i = 0; i < paragraphs.length; i++) {
    paragraphs[i].style.width = `${box.offsetWidth}px`;
  }
}

Esse código passa por um grupo de parágrafos e define a largura de cada parágrafo para que corresponda à largura de um elemento chamado "caixa". Parece inofensivo, mas o problema é que cada iteração da repetição lê um valor de estilo (box.offsetWidth) e o usa imediatamente para atualizar a largura de um parágrafo (paragraphs[i].style.width). Na próxima iteração do loop, o navegador precisa considerar que os estilos mudaram desde a última solicitação de offsetWidth (na última iteração). Por isso, ele precisa aplicar as mudanças de estilo e executar o layout. Isso vai acontecer em cada iteração!

A correção para este exemplo é novamente read e depois write os valores:

// Read.
const width = box.offsetWidth;

function resizeAllParagraphsToMatchBlockWidth () {
  for (let i = 0; i < paragraphs.length; i++) {
    // Now write.
    paragraphs[i].style.width = `${width}px`;
  }
}

Se você quiser garantir a segurança, use o FastDOM, que agrupa automaticamente leituras e gravações em lote e impede que você acione layouts síncronos forçados ou troca frequente de layouts.

Imagem principal do Unsplash, de Hal Gatewood.