espera aí...

15 de janeiro de 2019
Otimizações de Renderização de Navegador para o Desenvolvimento Frontend
Otimizações de Renderização de Navegador para o Desenvolvimento Frontend

Vivemos em uma época em que a importância de fornecer serviços da Web em velocidade ideal não pode ser subestimada. À medida que a carga transmitida pelos aplicativos da Web aumenta, os desenvolvedores devem adotar práticas recomendadas para garantir que os pacotes de dados sejam entregues quase instantaneamente, proporcionando aos usuários uma experiência geral exemplar.

Algumas das práticas recomendadas amplamente adotadas no desenvolvimento da web hoje são compactação de imagem, minificação de código, agrupamento de código (com ferramentas como Webpack) etc. Essas práticas já têm o efeito de melhorar a satisfação do usuário, mas é possível obter mais quando o desenvolvedor compreende as etapas subjacentes que orientam a renderização de aplicativos da Web para o DOM.

Ao jogar um jogo intensivo de GPU em um dispositivo de computação de baixo custo, podemos experimentar algum tremor – vibração ou vibração – dos personagens do jogo e do ambiente. Esse comportamento (embora não tão óbvio) também é possível em aplicativos da Web; os usuários podem perceber quando o aplicativo para por um segundo ou dois e atrasos em responder a uma atividade interativa, como um clique ou rolagem.

Neste artigo, discutiremos as condições que podem permitir (e impedir) que um aplicativo da Web seja executado (de maneira ideal) a 60 quadros por segundo.

O que entra em um quadro?

Sempre que há uma mudança visual em um aplicativo da Web, o que acontece sob o capô é: o navegador coloca um novo quadro para o usuário ver e interagir. A taxa na qual esses quadros aparecem (e são atualizados) é medida em quadros por segundo (fps). Se o navegador demorar muito para criar e renderizar um quadro, o fps cairá e o usuário poderá notar a trepidação do aplicativo.

Para criar aplicativos da Web com alto desempenho que são executados a 60 quadros por segundo, o desenvolvedor precisa entender o conteúdo de um quadro. Aqui está um detalhamento (em 5 etapas) de como um quadro é criado:

  1. O navegador faz uma solicitação GET para um servidor remoto.
  2. O servidor responde com algum HTML e CSS.
  3. O HTML é analisado no DOM.
  4. O CSS é analisado em um modelo de objeto CSS (CSSOM) e integrado ao DOM para criar uma nova árvore chamada Render tree.
  5. A árvore Render consiste essencialmente nos elementos exibidos na página e formam um quadro.

Observação: a etapa 4 é mostrada nas ferramentas de desenvolvimento doChrome como Recalcular estilos

App Lifecycle

Antes de continuarmos a explorar o caminho de renderização do navegador e as otimizações que podem ser conectadas a ele, precisamos aprender sobre o ciclo de vida do aplicativo, pois isso nos permitiria fazer escolhas inteligentes ao determinar quando um aplicativo deveria realizar o “trabalho pesado”. criando assim uma experiência de usuário suave e aumentando a satisfação do usuário.

O ciclo de vida do aplicativo é dividido em quatro etapas:

  1. Carga
  2. Ocioso
  3. Animação
  4. Resposta

Carga

Antes que um usuário possa interagir com um aplicativo da Web, ele deve ser carregado primeiro. Este é o primeiro estágio no ciclo de vida do aplicativo e é importante ter como objetivo reduzir (idealmente em 1s) o tempo de carregamento para o menor número possível.

Ocioso

Depois que um aplicativo é carregado, ele geralmente fica ocioso; esperando o usuário interagir com ele. O bloco ocioso geralmente tem cerca de 50ms de comprimento e fornece ao desenvolvedor a oportunidade de realizar o trabalho pesado, como o carregamento dos recursos (imagens, vídeos, seção de comentários) que um usuário pode acessar mais tarde.

ProTip: Um truque para reduzir significativamente o tempo de carregamento é carregar apenas os fundamentos da interface do usuário e puxar outros elementos no estágio Ocioso.

Animar

Quando o usuário começa a interagir com o aplicativo e o estágio Ocioso termina, o aplicativo precisa reagir adequadamente à interação do usuário (e entrada) sem qualquer atraso visível.

Nota: Estudos mostraram que leva cerca de um décimo de segundo (depois de interagir com um elemento de interface do usuário) para notar qualquer atraso. Portanto, responder à entrada do usuário neste intervalo de tempo é perfeito.

Um desafio pode ser colocado quando a resposta à interação do usuário envolve algum tipo de animação. Para renderizar animações executadas a 60 quadros por segundo, cada quadro teria um limite de 16 ms – este é basicamente um segundo dividido por 60.

Na realidade, isso deve ser de 10 ms a 12 ms devido à sobrecarga do navegador. Uma maneira de conseguir isso seria executar todos os cálculos de animação antecipadamente (durante os 100ms após a interação de um elemento da interface do usuário).

O caminho de renderização do navegador e várias otimizações

O caminho de renderização do navegador segue a seguinte rota:

  • JavaScript
  • Cálculo de estilo
  • Criação de layout
  • Pintura de pixels da tela
  • Composição da camada

Em uma página da Web, quando uma alteração visual é feita (por CSS ou JavaScript), o navegador recalcula os estilos dos elementos afetados. Se houver alterações na geometria de um elemento, o navegador verificará os outros elementos, criará um novo layout, repintará os elementos afetados e recompactará esses elementos juntos.

No entanto, alterar certas propriedades dos elementos de uma página pode alterar o caminho de renderização de uma página da web. Por exemplo, se uma propriedade somente de pintura, como imagem de plano de fundo ou cor de texto, for alterada, o layout não será afetado porque nenhuma alteração foi feita na geometria do elemento. Outras alterações de propriedade podem deixar a geração de layout e pintar o pipeline de renderização.

Vamos expor algumas otimizações que podem ser conectadas ao caminho de renderização do navegador.

JavaScript

O JavaScript permite que os desenvolvedores forneçam aos usuários animações incríveis e experiências visuais e, portanto, é muito usado em aplicativos da web. A partir de nossa discussão sobre o ciclo de vida do aplicativo, vemos que o navegador tem cerca de 10ms a 12ms para renderizar cada quadro. Para diminuir o peso do JavaScript no pipeline de renderização, é importante executar todo o código JavaScript o mais cedo possível em cada quadro, já que ele poderia acionar outras áreas do pipeline de renderização.

É possível conseguir isso usando o window.requesAnimationFrame()método, de acordo com os documentos da Web do MDN :

“O window.requestAnimationFrame()método informa ao navegador que você deseja executar uma animação e solicita que o navegador chame uma função especificada para atualizar uma animação antes da próxima repintura. O método recebe um retorno de chamada como argumento a ser invocado antes da repintura. ”

requestAnimationFrame()API permite que o navegador traga JavaScript no momento certo e evite a falta de um quadro. Aqui está um exemplo do método em uso:

function doAnimation() {
    // Some code wizardry
    requestAnimationFrame(doAnimation); //schedule the next frame

}

requestAnimationFrame(doAnimation);

A guia de desempenho nas ferramentas de desenvolvimento do Chrome permite que os desenvolvedores gravem uma página enquanto estão em uso e exibem uma interface que mostra como o JavaScript funciona no aplicativo da web.

Embora requestAnimationFrameseja uma ferramenta muito importante, alguns códigos JavaScript podem ser muito intensivos em recursos. Os websites são executados no thread principal de nossos sistemas operacionais, portanto, esses scripts podem interromper a execução de outros estágios do pipeline de renderização. Para resolver este problema, podemos usar trabalhadores da web.

Os funcionários da Web nos permitem gerar novos encadeamentos para o código JavaScript de uso intensivo de recursos, de acordo com os documentos da Web do MDN:

“Web Workers é um meio simples de conteúdo da Web para executar scripts em encadeamentos em segundo plano. O thread de trabalho pode executar tarefas sem interferir na interface do usuário. Uma vez criado, um trabalhador pode enviar mensagens para o código JavaScript que o criou, enviando mensagens para um manipulador de eventos especificado por esse código (e vice-versa). ”

Para usar esse recurso, você precisará criar um arquivo JavaScript separado, o qual seu aplicativo principal gerará em um web worker.

Cálculo de estilo

As alterações de estilo são uma parte essencial do pipeline de processamento de qualquer aplicativo da Web, pois o número de alterações de estilo requeridas por seus elementos é diretamente proporcional ao custo de desempenho do recálculo de estilo. Confira o site de Paul para uma análise bastante decente de como os estilos CSS afetam o pipeline de renderização e, portanto, o desempenho.

Além do número de alterações de estilo, a correspondência de seletores deve ser fatorada em nossa lista de otimizações de renderização. A correspondência de seletor refere-se ao processo de determinar quais estilos devem ser aplicados a qualquer elemento DOM.

Certos estilos podem levar mais tempo para serem processados ​​do que outros e isso se torna importante à medida que o número de elementos afetados por uma ou mais mudanças de estilo aumenta. Uma abordagem adequada para resolver esse problema é a metodologia Block Element Modifier (BEM) . Ele oferece grandes benefícios para o desempenho, pois a correspondência de classe, que segue a metodologia BEM, é o seletor mais rápido para corresponder aos navegadores modernos.

Criação de Layout

Um grande gargalo de desempenho é a divisão de layout. Isso ocorre quando solicitações de valores geométricos intercalados com alterações de estilo são feitas em JavaScript e faz com que o navegador reflua o layout. Isso, quando feito várias vezes em sucessões rápidas, leva a um layout síncrono forçado. Neste artigo , o Googler, Paul Lewis , destaca as várias otimizações que podem ser feitas para evitar layouts síncronos forçados.

Pintura de pixels de tela

A pintura ocorre quando o navegador começa a preencher os pixels da tela. Isso envolve o desenho de todos os elementos visuais na tela. Isso é feito em várias superfícies, chamadas camadas. O Paint pode causar problemas de desempenho quando é necessário em grandes partes da página, especialmente em intervalos, como visto durante a rolagem da página.

O perfilador de tinta, como mostrado acima, facilita a identificação de quais áreas da página estão sendo pintadas e quando estão sendo pintadas. O criador de perfil de pintura pode ser encontrado pressionando a tecla de escape depois de navegar para as ferramentas do Chrome Dev e selecionando a guia Renderização.

A primeira caixa de seleção, Paint piscando, realça em verde quais áreas da página estão pintadas no momento e a frequência disso pode nos dizer se a pintura contribui para os problemas de desempenho no pipeline de renderização.

Olhando para a imagem acima, podemos ver que apenas a barra de rolagem é pintado como a página é rolada, o que indica grandes otimizações pintura navegador na página inicial do Scotch.io website 😄.

Composição de Camadas

Esse é o caminho final no pipeline de renderização do navegador e envolve uma estrutura importante do navegador – Camadas. O mecanismo do navegador faz algum gerenciamento de camadas considerando primeiro os estilos e elementos e como eles são ordenados, depois tenta descobrir quais camadas são necessárias para a página e atualiza a árvore de camadas de acordo.

Em seguida, o navegador compõe essas camadas e as exibe na tela. Os afunilamentos de desempenho devido à pintura ocorrem quando o navegador precisa pintar elementos de página que se sobrepõem e também existem na mesma camada um do outro.

Para resolver este problema, os elementos envolvidos terão que existir em camadas separadas. Isso pode ser conseguido com a will-changepropriedade CSS e definindo seu atributo para transform:

<element_to_promote> {
    will-change: transform;
}

Deve-se notar, no entanto, que um aumento nas camadas significaria um aumento no tempo gasto em gerenciamento de camadas e composição. Com as ferramentas de desenvolvimento do Chrome, é possível ver todas as camadas em uma página, conforme mostrado abaixo:

Para chegar à guia “Camadas”, clique no botão do menu do hamburger (três pontos verticais) nas ferramentas do Chrome Dev, navegue até mais ferramentas e selecione Camadas.

Conclusão

Fizemos um breve passeio pelo pipeline de renderização do navegador, o ciclo de vida do aplicativo e as várias otimizações que podem ser conectadas ao pipeline de renderização durante o ciclo de vida de animação de um aplicativo da web.

Quando essas otimizações são implementadas logicamente, o resultado é uma ótima experiência do usuário e alguma satisfação interna para o desenvolvedor frontend. Se a leitura deste artigo tiver despertado seu interesse, você poderá aprender mais sobre as otimizações de renderização do navegador deste curso .

Há uma sequela para este artigo que se concentra em usar o Chrome Dev Tools para encontrar gargalos de desempenho. Você pode ler sobre isso aqui.

Posted in Blog
Write a comment