Evite layouts grandes e complexos e a troca frequente de layouts

O layout é onde o navegador descobre informações geométricas dos elementos, ou seja, 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.

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 em navegadores derivados, como o Edge) e Safari. No Firefox, chama-se Reflow, mas o processo é basicamente o mesmo.

Da mesma forma que os 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 afeta 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íveis. O tempo necessário para uma interação ser concluída, que termina quando o navegador apresenta o próximo frame para mostrar os resultados, é 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 as atualizações visuais podem envolver uma quantidade de trabalho de layout para alcançar esse objetivo.

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

Evite o layout sempre que possível

Quando você altera estilos, o navegador verifica se alguma mudança exige que o layout seja calculado e que a árvore de renderização seja atualizada. Mudanças nas "propriedades geométricas", como larguras, alturas, esquerda ou topo, 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 é todo o documento. 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, a chave é usar novamente o Chrome DevTools para ver quanto tempo está demorando e determinar se o layout é a causa de um gargalo. Primeiro, abra o DevTools, acesse a guia Timeline, clique em Record e interaja com o site. Quando você parar de gravar, verá um detalhamento do desempenho do seu site:

DevTools mostrando um longo tempo no Layout.

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

Tenha em mente que o conselho geral aqui é evitar o layout sempre que possível, mas nem sempre é possível evitar o layout. Nos casos em que não é possível evitar o layout, saiba que o custo dele tem relação com o tamanho do DOM. Embora a relação entre os dois não esteja rigidamente acoplada, DOMs maiores geralmente incorrem em custos de layout maiores.

Evitar layouts síncronos forçados

O envio de um frame para a tela tem esta 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 um navegador a executar o layout mais cedo com o JavaScript. Isso é chamado de layout síncrono forçado.

Tenha em mente que, à medida que o JavaScript é executado, todos os valores de layout antigos do frame anterior são conhecidos e ficam 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 como este:

// 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);
}

O processo pode ser complicado 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 de 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 a altura correta poderá ser retornada. Esse trabalho é desnecessário e potencialmente caro.

Por isso, é preciso sempre agrupar as leituras de estilo e executá-las primeiro (quando o navegador pode usar os valores de layout do frame anterior) e depois fazer as gravações:

Executada 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, não é necessário aplicar estilos e consultar valores. O uso dos valores do último frame deve ser suficiente. Executar os cálculos de estilo e o layout de forma síncrona e antes do momento em que o navegador gostaria de ser executado pode criar possíveis gargalos e não é algo que você normalmente precisa fazer.

Evitar a troca frequente de layouts

Há uma maneira de piorar 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 de forma correspondente à 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 que offsetWidth foi solicitado pela última vez (na iteração anterior) e, portanto, precisa aplicar as mudanças de estilo e executar o layout. Isso vai acontecer em todas as iterações.

Para corrigir o exemplo, novamente read e write será aplicado:

// 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 (link em inglês), que agrupa automaticamente suas leituras e gravações e impede o acionamento acidental de layouts síncronos forçados ou a troca frequente de layouts.

Imagem principal do Unsplash, de Hal Gatewood.