Allysson Santos

Front-end Developer

Critical rendering path - Renderização no browser.

Introdução:

Entenda o que é o caminho de renderização crítica e como isso afeta a performance do seu site.

Critical rendering path - Renderização no browser.

Caminho crítico de renderização

O caminho crítico de renderização é a sequência de etapas que o navegador executa para converter o HTML, CSS e Javascript em pixels na tela. Vamos conhecer essas etapas e como podemos renderizar as nossas páginas mais rápido.

As etapas que o browser realiza para renderizar nossa página são:

  • Pegar o HTML e começar a criar o Document Object Model. (DOM)
  • Extrair o CSS e criar o Css Object Model (CSSOM)
  • Combinar o DOM e CSSOM para criar a árvore de renderização. (Rendering tree)
  • Descobrir onde tudo se encaixa na página (Layout)
  • Pintar os pixels na tela (Paint)

Também temos a etapa de como o browser lida com o javascript, falaremos sobre isso mais à frente.

Convertendo HTML para DOM

Quando acessamos uma página da web, o browser recebe a resposta da nossa requisição (conteúdo HTML) e transforma em pixels, vamos descobrir como isso é feito.

A especificação do HTML possui uma série de regras sobre como o browser deve processar o conteúdo recebido, por exemplo, o texto contido entre sinais de menor < e maior > possui um significado especial no HTML, isso deve ser considerado uma TAG, como resultado, toda vez que o browser encontra uma TAG ele emite um Token, vejamos um exemplo:

1
2
3
4
5
6
<html>
  <head>
  <meta name="viewport" content="width=device-width">
  <link href="style.css" rel="stylesheet">

  ...

Após o browser receber o nosso html ele analisa a primeira TAG <html> e emite um token, existem dois tipos de tokens o StartTag e o EndTag, nesse caso, o primeiro token é o StartTag: HTML, o segundo StartTag: head e assim por diante. Esse processo inteiro é feito pelo tokenizer e enquanto o tokenizer está fazendo esse trabalho, existe outro processo que está consumindo esses tokens e convertendo em Node Objects. Por exemplo: o browser converte o primeiro token StartTag: HTML e cria o HTML Node, depois consome o pŕoximo token e cria o node correspondente. Perceba que o tokenizer emite Start e End tokens, que nos mostra a relação entre os nós (nodes). O token StartTag: head vem antes do token EndTag: HTML o que nos diz que o token head é um filho do token html, nessa lógica, vemos que o node da tag meta e o node da tag link são filhos de head, e assim por diante.

Veja um esquema desse processo aplicado à uma página web simples:

Processo do Critical Rendering Path

Depois de consumido todos os tokens e todos os nodes criados, chegamos ao DOM, que é a estrutura em árvore que captura o conteúdo e propriedades do HTML e todas suas relações entre os nodes.

No DevTools do navegador Google Chrome existe uma aba chamada Performance nela é possível consultar o tempo necessário que o browser levou para executar todo esse processo que vimos acima:

Timeline

O browser constrói o DOM incrementalmente, e nós podemos tirar vantagem disso e aumentar a velocidade da renderização. Um exemplo é como a página de resultados do Google trabalha, assim que você está digitando o que quer procurar o servidor do google já entrega o header da página, que é igual para todos os usuários, assim, quando você terminar de escrever o que deseja buscar, o servidor envia os dados em HTML e o browser incrementa o DOM com os resultados. Como você pode notar, o servidor do google não precisa esperar que você envie o que deseja buscar para retornar o HTML completo para o browser processar tudo de uma vez, ele começa com o header da página, e incrementalmente devolve o resultado da sua busca. Portanto, entregar HTML em parcelas é algo muito bom!

CSSOM - Convertendo CSS em CSS Object Model

Bom, o DOM captura o conteúdo da página, mas nós também precisamos saber como exibi-la e onde encaixar as coisas, e para isso, o browser precisa criar o CSS Object Model, o CSSOM é bem parecido com o DOM, porém, DOM e CSSOM são estruturas independentes! Vejamos:

Quando o navegador está construindo o DOM e encontra uma tag link que referencia alguma folha de estilo CSS ele prevê que esse recurso é necessário para renderizar a página e envia imediatamente uma requisição para esse arquivo, segue um exemplo do que essa folha de estilos pode retornar:

1
2
3
4
5
body { font-size: 16px }
p { font-weight: bold }
span { color: red }
p span { display: none }
img { float: right }

Assim como o HTML o navegador precisa converter as regras CSS em algo que ele consiga entender e utilizar. Portanto, o processo feito no HTML é repetido, só que dessa vez, para o CSS.

CSSOM

Como vemos, segue o mesmo padrão utilizado no HTML, só que ao invés de criar a estrutura do DOM, é criada a estrutura do CSSOM.

Árvore CSSOM

Com o esquema acima, conseguimos entender o porque o CSS é aplicado em cascata. O estilo aplicado ao elemento body é herdado por todos os seus filhos, analisando a árvore CSS acima, todo texto dentro do elemento span terá fonte tamanho 16 pixels (estilo herdado do elemento body) e a cor vermelha (estilo específico para o elemento span).

Há um detalhe muito importante, todo navegador possui um conjunto de regras CSS padrões, conhecidos também por estilos de user-agent (por exemplo estilos padrões do Internet Explorer)

Assim como é possível descobrir o tempo necessário para o parse do HTML, também é possível verificar o tempo necessário para o processamento do CSS através do DevTools:

Timeline do CSSOM

É possível analisar o tempo decorrido e também quantos elementos foram afetados. Com isso, podemos concluir que escrever um CSS de qualidade, com seletores específicos, evitar sobreescritas e evitar !important é de grande importância, devemos afetar apenas os elementos corretos para diminuir o tempo necessário de processamento do CSSOM.

Até aqui vimos a construção dos modelos de objetos, criamos as árvores do DOM e do CSSOM. No entanto, ambos são objetos independentes: um descreve o conteúdo e o outro, as regras de estilos que devem ser aplicadas ao documento. Para entender a mescla entre o DOM e o CSSOM precisamos conhecer a árvore de renderização

Árvore de renderização

A primeira tarefa que o browser efetua quando está nessa fase é a combinação do DOM e CSSOM em uma árvore de renderização que contém todo conteúdo visível do DOM e todas regras de CSS do CSSOM de cada nó.

Árvore de renderização Exemlo de uma árvore de renderização

As demais tarefas são as seguintes:

  1. A partir do começo do DOM, percorre cada elemento (nó) visível.
    • Alguns elementos não são visíveis, como head, script meta, etc, e são omitidos pois não são refletidos no resultado da renderização.
    • Os elementos que forem ocultados via CSS também são omitidos no resultado da renderização, no exemplo acima, uma das tags span está omitida, porque existe uma regra CSS que define a propriedade display: none.
  2. Para cada nó visível é encontrada e aplicada a sua regra do CSS correspondente.
  3. Por fim, retorna os nós visíveis e seus estilos aplicados.

Observação: a regra CSS visibility: hidden é diferente da regra display: none. A primeira torna o elemento invisível, o elemento ainda ocupa o seu espaço no layout, ou seja, é renderizado como uma caixa vazia. O segundo remove completamente o elemento da árvore de renderização, o elemento fica invisível e não faz parte do layout.

Por fim, temos o resultado da renderização com o conteúdo e toda informação de estilo do conteúdo visível na tela. Podemos passar para fase de layout.

Layout

Até agora o browser calculou quais nós devem ser visíveis e processou seus estilos. O browser precisa agora calcular a posição e o tamanho que irá ocupar no viewport (espaço disponível do dispositivo), essa fase é o layout, também conhecida como reflow.

Para calcular a posição e o tamanho exato de cada elemento, o browser analisa a árvore de renderização passando por toda ela a partir de sua raiz. Vamos analisar esse simples exemplo:

1
2
3
4
5
6
7
8
9
10
11
<html>
  <head>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>Critial Path: Hello world!</title>
  </head>
  <body>
    <div style="width: 50%">
      <div style="width: 50%">Hello world!</div>
    </div>
  </body>
</html>

O HTML acima possuí dois divs aninhados. O primeiro (pai) define o tamanho do elemento na tela como 50% da largura da janela do dispositivo (viewport). O segundo div (filho) define sua largura como 50% do seu pai, ou seja, 25% da largura do viewport.

Layout viewport

O resultado da renderização é em modelo de caixa, ou seja, todos elementos são retângulos. Todas as medidas relativas, como porcentagens, em’s, rem’s, etc são convertidas em pixels absolutos na tela.

Agora que o browser possui todos os nós visíveis, seus estilos aplicados e sua geometria, finalmente chega a última fase, essa etapa é chamada de Paint.

Paint

O paint é o processo que o browser executa para aplicar cada nó da árvore de renderização em pixeis reais na tela do dispositivo do usuário. Assim como os outros processos, no DevTools do Chrome, também é possível examinar o tempo necessário para a fase de Layout e Paint.

Layout Timeline

  • O evento layout captura exibe o tempo e a quantidade de nós que foram construídos, posicionados e tiveram seu tamanho calculado a partir da árvore de renderização.
  • Assim que o layout termina, o browser emite eventos de Paint setup e Paint, esses eventos convertem a árvore de renderização em pixeis na tela.

O tempo nececssário para executar essa renderização varia de acordo com a quantidade de elementos que temos em nosso HTML, da complexidade do CSS e do tamanho do viewport do dispositivo. Quanto maior o HTML, maior trabalho para o navegador. Quanto mais complexo o estilo, mais tempo é necessário na etapa de Paint. Fazer a renderização de uma cor sólida (#000) é pouco custosa, porém, a renderização de uma sombra (box-shadow) é bem mais complexa e custosa.

E com isso descobrimos todo o trabalho que o browser necessita realizar para renderizar nossa página na tela do usuário. No próximo post veremos o que gera o famoso “Bloqueio de renderização” ou “Bloqueio do caminho critíco de renderização” e como podemos com medidas simples, solucionar isso.

comments powered by Disqus