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:
- É o número de elementos que exigem layout, que é um subproduto do tamanho do DOM da página.
- 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:
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:
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.