Entendendo os Reflows

O que são e como os Reflows podem ser otimizados para aplicações ficarem ainda mais rápidas.

por Alysson Franklin 18/07/2011 ~ 10 min. / 1988 palavras

Reflow é um assunto extenso e necessário. Ele sempre vai existir nos navegadores, então temos que entendê-lo para saber como utilizá-lo de maneira racional. O mais legal é entender todo o contexto sobre o que são e como funcionam, para a partir daí repensar o código que renderizamos no navegador para obtermos maior performance.

Reflow é o resultado de um evento que desencadeia mudanças no jeito que a pagina deve ser renderizada, tomando tempo para cálculo e reposicionamento de elementos.

Para explicar como isso acontece, o importante é entender como um navegador renderiza uma página web.

DOM

Document Object Model (DOM) é uma interface independente de linguagem e plataforma para representar e interagir com objetos em HTML, XHTML e XML. Mas o DOM é mais que isso; toda linguagem estruturada tem uma arvore DOM.

Mas sobre o DOM podemos dizer que programas ou scripts podem dinamicamente acessar elementos na árvore DOM e alterar seu conteúdo, estrutura e estilo. Adicional ao estado inicial da pagina, estas alterações são agregadas a árvore DOM. O resultado é a renderização – o que nós vemos em um navegador. Mas a nossa Render Tree tem mais que isso:

Render Tree e como realmente entender display X visibility

O DOM é resultado do parse do markup HTML que você montou dentro de um navegador. Só que dentro de um HTML você não tem apenas a estrutura do documento, estilos em CSS e funcionalidades em javascript também estarão no seu markup. E a Render tree é a soma de DOM mais estilos, que depois podem ser manipulados (seus scripts).

Pode parecer estranho, mas para mim a melhor maneira de entender as diferencas entre DOM e Render Tree é encarar esta última como um individuo narcisista. Isso mesmo: A Render Tree gosta de aparecer, é o que vemos no browser. O DOM acontece por trás das cortinas. À Render tree, o palco. (Para saber mais sobre o DOM, veja este outro artigo)

Uma página que tem controles de show/hide mostra isso muito bem. Enquanto todo o conteúdo da página está presente na árvore DOM, apenas alguns itens estarão disponíveis na Render Tree – os visíveis na página (display:block). Alterar os elementos display:none vão disparar mudanças na render tree, e não na árvore DOM.

Já tentou entender as diferenças entre display:none e visibility:hidden? A maioria das explicações que vemos é que um “deixa de mostrar o elemento totalmente, incluindo seu espaço em tela” e o outro “deixa de mostrar o elemento visual, mas sua área de exibição continua sendo utilizada”. Esta explicação não está incorreta, mas seria melhor dizer que a propriedade display altera a estrutura da render tree, adicionando algo que antes não estava ali, enquanto visibility não faz alterações, apenas o câmbio de mostrar ou não um elemento que já esta na render tree.

Repaint

Entendido as diferenças entre display e visibility, além do contexto de área do elemento, conseguimos explicar o Repaint. Uma ação que faça alteração de propriedades de background sem alterar dimensões ou outras propriedades do elemento não causam o reflow, apenas o repaint, que seria a atualização da propriedade de cor ou da imagem – literalmente pintar novamente o elemento. Repaints gastam menos CPU que reflows.

Como um navegador monta seu documento HTML

Esses vídeos são sensacionais. Eles explicam o que são os reflows e dai como minimizá-los durante a fase de desenvolvimento do documento (sempre usando Progressive Enhancement quando possível) é mais fácil..

[youtube https://www.youtube.com/watch?v=ZTnIxIA5KGw]

Analisando o primeiro vídeo, perceba que ao finalizar a montagem do rodapé, “algo mais” acontece (a partir de 12seg). São os Reflows. A maioria dos elementos são recalculados e reposicionados. Se ponderarmos que o rodapé acaba de ser montado aos 14 segundos e a renderização termina aos 26 segundos da pagina, estamos falando quase de 50% do tempo de renderização sendo gasto com Reflows, o que é muito, dependendo do que a sua página deve fazer.

O que causam exatamente os reflows?

Reflow é o resultado de um evento que desencadeia mudanças no jeito que a pagina deve ser renderizada, tomando tempo para cálculo e reposicionamento de elementos.

Para explicar como isso acontece, o importante é entender como um navegador renderiza uma página web.

DOM

Document Object Model (DOM) é uma interface independente de linguagem e plataforma para representar e interagir com objetos em HTML, XHTML e XML. Mas o DOM é mais que isso; toda linguagem estruturada tem uma arvore DOM.

Mas sobre o DOM podemos dizer que programas ou scripts podem dinamicamente acessar elementos na árvore DOM e alterar seu conteúdo, estrutura e estilo. Adicional ao estado inicial da pagina, estas alterações são agregadas a árvore DOM. O resultado é a renderização – o que nós vemos em um navegador. Mas a nossa Render Tree tem mais que isso:

Render Tree e como realmente entender display X visibility

O DOM é resultado do parse do markup HTML que você montou dentro de um navegador. Só que dentro de um HTML você não tem apenas a estrutura do documento, estilos em CSS e funcionalidades em javascript também estarão no seu markup. E a Render tree é a soma de DOM mais estilos, que depois podem ser manipulados (seus scripts).

Pode parecer estranho, mas para mim a melhor maneira de entender as diferencas entre DOM e Render Tree é encarar esta última como um individuo narcisista. Isso mesmo: A Render Tree gosta de aparecer, é o que vemos no browser. O DOM acontece por trás das cortinas. À Render tree, o palco. (Para saber mais sobre o DOM, veja este outro artigo)

Uma página que tem controles de show/hide mostra isso muito bem. Enquanto todo o conteúdo da página está presente na árvore DOM, apenas alguns itens estarão disponíveis na Render Tree – os visíveis na página (display:block). Alterar os elementos display:none vão disparar mudanças na render tree, e não na árvore DOM.

Já tentou entender as diferenças entre display:none e visibility:hidden? A maioria das explicações que vemos é que um “deixa de mostrar o elemento totalmente, incluindo seu espaço em tela” e o outro “deixa de mostrar o elemento visual, mas sua área de exibição continua sendo utilizada”. Esta explicação não está incorreta, mas seria melhor dizer que a propriedade display altera a estrutura da render tree, adicionando algo que antes não estava ali, enquanto visibility não faz alterações, apenas o câmbio de mostrar ou não um elemento que já esta na render tree.

Repaint

Entendido as diferenças entre display e visibility, além do contexto de área do elemento, conseguimos explicar o Repaint. Uma ação que faça alteração de propriedades de background sem alterar dimensões ou outras propriedades do elemento não causam o reflow, apenas o repaint, que seria a atualização da propriedade de cor ou da imagem – literalmente pintar novamente o elemento. Repaints gastam menos CPU que reflows.

Como um navegador monta seu documento HTML

Esses vídeos são sensacionais. Eles explicam o que são os reflows e dai como minimizá-los durante a fase de desenvolvimento do documento (sempre usando Progressive Enhancement quando possível) é mais fácil..

[youtube https://www.youtube.com/watch?v=ZTnIxIA5KGw]

Analisando o primeiro vídeo, perceba que ao finalizar a montagem do rodapé, “algo mais” acontece (a partir de 12seg). São os Reflows. A maioria dos elementos são recalculados e reposicionados. Se ponderarmos que o rodapé acaba de ser montado aos 14 segundos e a renderização termina aos 26 segundos da pagina, estamos falando quase de 50% do tempo de renderização sendo gasto com Reflows, o que é muito, dependendo do que a sua página deve fazer.

O que causam exatamente os reflows?

#####`

Reflows são excessivamente pesados e para reduzir efeitos uma das táticas que navegadores usam é processar nossos scripts em lote. Uma fila é criada para todos os comandos que causam reflow sejam processados de uma única vez. Porém o foco é entender o que causa um reflow e tentar minimizar o seu uso para ganhar performance na aplicação.

Este assunto é novo, e com certeza, A lista que mostro abaixo deve crescer. É importante mantermos a atenção a este assunto porque pequenos cuidados podem significar muito. Em um site web visualizado em desktops a diferença é óbvia sobre o tempo de renderização. Mas isso implica em outras coisas, que podem fazer a diferença não apenas em montar uma página mais rápido, mas também para menor gasto de processamento, o que garante também mais tempo de bateria em mobiles e tablets por exemplo.

Em um dos posts sobre o assunto, Tony G mapeou pesquisas prévias e montou a seguinte tabela, que também está sendo constantemente atualizada.

<td>
  <h4>
    <strong>Frame, Image</strong>
  </h4>
</td>

<td>
  <h4>
    <strong>Range</strong>
  </h4>
</td>

<td>
  <h4>
    <strong>SVGLocatable</strong>
  </h4>
</td>

<td>
  <h4>
    <strong>SVGTextContent</strong>
  </h4>
</td>

<td>
  <h4>
    <strong>SVGUse</strong>
  </h4>
</td>

<td>
  <h4>
    <strong>window</strong>
  </h4>
</td>
<td>
  <span class="c0">height, width</span>
</td>

<td>
  <span class="c0">getBoundingClientRect(), getClientRects()</span>
</td>

<td>
  <span class="c0">computeCTM(), getBBox()</span></p> 
  
  <p class="c5">
    </td> 
    
    <td>
      <span class="c0">getCharNumAtPosition(), getComputedTextLength(), getEndPositionOfChar(), getExtentOfChar(), getNumberOfChars(), getRotationOfChar(), getStartPositionOfChar(), getSubStringLength(), selectSubString()</span>
    </td>
    
    <td>
      <span class="c0">instanceRoot</span>
    </td>
    
    <td>
      <span class="c0">getComputedStyle(),<br /> scrollBy(),<br /> scrollTo(),<br /> scrollX,<br /> scrollY, webkitConvertPointFromNodeToPage(), webkitConvertPointFromPageToNode()</span>
    </td></tr> </tbody> </table> 
    
    <h2>
      Como melhorar o meu código para minimizar os reflows?
    </h2>
    
    <p>
      É simples. Basta minimizar o uso de requisições de estilo, que façam o navegador executar reflows ou repaints.
    </p>
    
    <ol start="1">
      <li>
        <span class="c11">Planejar a sua aplicação e entender como plugins e scripts criados vão se comportar em relação a reflow e repaints. Arquitetar o uso de plugins de acordo com a personalização que deve ser feita. Minimize o uso de alteração de estilos on-the-fly.</span>
      </li>
      <li>
        <span class="c11">Quando precisar alterar a propriedade de um estilo, troque o nome da classe, planeje a existência deste estado e adicione-o ao CSS previamente. Se o valor desta nova classe for dinâmica, use cssText. Evite alterar a propriedade diretamente para qualquer mudança.</span>
      </li>
      <li>
        <span class="c0">Pense como suas mudanças afetam a render tree e o quanto precisará ser revalidado depois desta mudanca. Se você usa position:absolute em um elemento, ele deixa de pertencer ao nó que está, e passa a ser filho do BODY. Alterá-lo então, não será tão custoso em termos de performance. Mesmo que alterações neste nó sobreponha outras areas, o reflow acontecerá apenas neste nó, e não em toda a render tree. </span>
      </li>
      <li>
        <span class="c11">Limpe seu CSS. Classes não utilizadas devem ser removidas.</span>
      </li>
      <li>
        <span class="c11">Reduza o número de mudanças no DOM. Ele vai causar mudanças estruturais em todas as outras etapas. E mais tempo de reflow. </span>
      </li>
      <li>
        <span class="c11">Animações na página, transições? Pondere sobre posicioná-la de maneira absoluta e trabalhar com ela a partir do BODY.</span>
      </li>
      <li>
        <span class="c11">Vá com calma nos seletores CSS &#8211; os descendentes em particular &#8211; pois usam maior poder de CPU para executar a tarefa (CPU = Bateria).</span>
      </li>
    </ol>
    
    <h2>
      Referências
    </h2>
    
    <ul>
      <li>
        <a href="https://en.wikipedia.org/wiki/Document_Object_Model">DOM</a> pela wikipedia
      </li>
      <li>
        <a href="https://video.google.com/videoplay?docid=-1471976166301235697#docid=1020647662203348823">Gecko Reflow</a>
      </li>
      <li>
        <a href="https://paulirish.com/2011/dom-html5-css3-performance/">DOM, HTML5, CSS3 e Performance</a> &#8211; <a href="https://dl.dropbox.com/u/39519/talks/gperf/index.html">Slides</a> por Paul Irish
      </li>
      <li>
        <a href="https://www.mozilla.org/newlayout/doc/reflow.html">Reflow</a> pelo Mozilla Labs
      </li>
      <li>
        <a href="https://ajaxian.com/archives/browser-reflows-how-do-they-affect-performance">Reflow e Repaint</a> na Ajaxian
      </li>
      <li>
        <a href="https://code.google.com/speed/articles/reflow.html">Reflow</a> pelo Google Code
      </li>
      <li>
        <a href="https://www.w3.org/DOM/">W3C Overview do DOM</a>
      </li>
      <li>
        <a href="https://www.dayofjs.com/videos/22158462/web-browsers_alex-russel">1 dia de javascript com Alex Russel: Como Navegadores Veem as suas Apps</a>
      </li>
      <li>
        <a href="https://gent.ilcore.com/2011/03/how-not-to-trigger-layout-in-webkit.html">Como (não) criar um layout no webkit por Tony G</a>
      </li>
      <li>
        <a href="https://www.webkit.org/blog/1091/more-web-inspector-updates/#timeline_panel">Usando a timeline panel em navegadores webkit</a>
      </li>
      <li>
        <a href="https://www.bookofspeed.com/">The book of Speed</a>
      </li>
      <li>
        <a href="https://www.phpied.com/rendering-repaint-reflowrelayout-restyle/">Reflow/Repaint</a> por Stoyan Stefanov
      </li>
      <li>
        <a href="https://calendar.perfplanet.com/2009/the-new-game-show-will-it-reflow/">Inconsistências dos navegadores em Reflows</a> por Stoyan Stefanov
      </li>
      <li>
        <a href="https://www.browserscope.org/?category=reflow">BrowserScope tests para reflows</a>
      </li>
      <li>
        <a href="https://www.youtube.com/watch?v=a2_6bGNZ7bA">Browsers para Web Developers</a> David Baron da Mozilla labs
      </li>
      <li>
        <a href="https://www.webkit.org/blog/114/webcore-rendering-i-the-basics/">Renderização no webkit, o básico</a>
      </li>
    </ul>