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 dimensionamento explícitas ou implícitas de acordo com o CSS usado, o conteúdo do elemento ou um elemento principal. 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 dimensionamento explícitas ou implícitas de acordo com o CSS usado, o conteúdo do elemento ou um elemento principal. O processo é chamado de "Layout no Chrome" (e navegadores derivados, como o Edge) e o Safari. No Firefox, é chamado de Reflow, mas o processo é 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 efeito direto na latência de interação
- Normalmente o escopo do layout é o documento todo.
- O número de elementos DOM afeta o desempenho. Evite 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 depois faça as mudanças.
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 pintura avalia.
O tempo que o navegador leva para apresentar o próximo frame em resposta a uma interação do usuário é conhecido como delay de 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 algum trabalho de layout para alcançar essa meta.
Para manter o INP do seu site o mais baixo possível, é importante evitar o layout sempre que possível. Se não for possível evitar o layout totalmente, é importante limitar o trabalho de layout para que o navegador possa apresentar o próximo frame rapidamente.
Evite o layout sempre que possível
Quando você muda de 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 topo, exigem layout.
.box {
width: 20px;
height: 20px;
}
/**
* Changing width and height
* triggers layout.
*/
.box--expanded {
width: 200px;
height: 350px;
}
O layout quase sempre tem escopo para todo o documento. Se você tiver muitos elementos, levará muito tempo para descobrir as localizações 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 "Timeline", clique em "Record" e interaja com o site. Quando você interromper a gravação, vai aparecer um detalhamento do desempenho do site:
Ao analisar o rastro no exemplo acima, vemos que mais de 28 milissegundos são gastos no layout de cada frame. Quando temos 16 milissegundos para exibir um frame em uma animação, 28 milissegundos é tempo demais. O DevTools também informa o tamanho da árvore (neste caso,1.618 elementos) e quantos nós precisavam de layout (5, neste caso).
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 do layout tem uma relação com o tamanho do DOM. Embora a relação entre os dois não seja muito estreita, DOMs maiores geralmente incorrem em custos de layout mais altos.
Evitar layouts síncronos forçados
O envio de um frame para a tela é feito nesta ordem:
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 o JavaScript. Isso é chamado de layout síncrono forçado.
A primeira coisa a se lembrar é que, à medida que o JavaScript é executado, todos os valores de layout antigos do quadro anterior são conhecidos e estão disponíveis para consulta. Portanto, se por exemplo você quiser exibir a altura de um elemento (vamos chamá-lo de "caixa") no início do quadro, poderá criar um código semelhante a 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);
}
As coisas ficam mais complicadas se você alterou os estilos da caixa antes de consultar a altura dela:
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 poder responder à consulta de altura, o navegador deve primeiro aplicar a mudança de estilo (por causa da adição da classe super-big
) e em seguida executar o layout. Somente então será possível retornar a altura correta. Esse trabalho é desnecessário e possivelmente 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:
Realizada 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 será necessário aplicar estilos e consultar valores. O uso dos valores do último quadro deverá ser suficiente. A execução dos cálculos de estilo e layout de forma síncrona e antes do momento escolhido pelo navegador é um possível gargalo e, normalmente, deve ser evitada.
Evitar a troca frequente de layouts
Há uma forma de piorar ainda mais os layouts síncronos forçados: executar vários 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`;
}
}
Este código faz um loop sobre um grupo de parágrafos e define cada largura de parágrafo para corresponder com a largura de um elemento chamado "caixa". Parece inofensivo, mas o problema é que cada iteração do loop 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 o 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 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ê quer garantir a segurança, use o FastDOM, que agrupa automaticamente suas leituras e gravações em lotes e evita que você acione acidentalmente layouts síncronos forçados ou troca frequente de layouts.