CRITICAL
Rendering Path
Hugo Deiró
hugodeiro.com
/hugodeiro
/hdeiro
/hdeiro
hugodeiro@gmail.com
/hdeiro
O que é Critical Rendering Path?
Uma série de recomendações pautadas em como o navegador funciona visando entregar páginas mais rápidas e/ou otimizadas
O Critical Rendering Path influencia em diversos aspectos, desde a User Experience (UX) até Search Engine Optimization (SEO)
Representação da atuação do
Critical Rendering Path
Já que o Critical Rendering Path atua sobre o processo de renderização, é bom entendermos sobre como ele se dá
Como se dá o processo de renderização?
Pipeline de Renderização
HTML Parsing / Construção do DOM
DOM ⇔ Document Object Model
CSS Parsing / Construção do CSSOM
CSSOM ⇔ Cascade Style Sheet Object Model
DOM + CSSOM
DOM + CSSOM
Após a "fusão" do DOM e CSSOM, a Render Tree conterá todos os nós necessários para a renderização
Após a construção da Render Tree, é feito o processo de Layout, também chamado de Reflow Processing
Durante esta etapa são calculados o tamanho e a posição de cada elemento da Render Tree
Após o Reflow, acontece o Painting Process, no qual os pixels são efetivamente renderizados em tela
Redimensionamentos e outras mudanças bruscas tendem a degradar a performance da aplicação
Isso acontece porque este tipo de mudança desencadeia a re-execução de todo processo de Reflow e Paint
Existem várias medidas que o desenvolvedor pode tomar para tornar sua aplicação web o mais próxima possível do caminho crítico de renderização
Shall we begin?
LISPECTOR, Daenerys
HTML
O HTML é um recurso bloqueador de renderização
Isso implica que o browser não irá renderizar nada até o DOM estar pronto.
Documente seu código
(mas cuidado)
Por questões de manutenção, é sempre bom você manter seu código comentado. Porém, se você não utilizar uma ferramenta de build automatizado com uma task para removê-los, eles gerarão uma carga extra para o browser que precisará interpretar os comentários
Minifique seu código
A minificação é um processo de remoção de quebras de linhas, espaços em brancos e name-mangling (troca de nomes) de possíveis elementos
Especifique o DTD
O Document Type Declaration (DTD), também conhecido como DOCTYPE. Essa declaração é utilizada para informar ao interpretador do browser que tipo de código está sendo apresentado (HTML, xHTML, SVG etc)
<!DOCTYPE html>
<html>
<head>
<title>Title of the document</title>
</head>
<body>
The content of the document......
</body>
</html>
Cuidado com o alt="..."
O atributo alt="..." é (ou pelo menos deveria ser) amplamente utilizado em imagens. Este atributo não só torna seu site mais acessível para pessoas com deficiência visual, mas também otimiza aspectos de SEO
O grande problema está em como estas descrições são aplicadas. Descrições longas podem degradar a performance e rastreabilidade da sua página web por deixarem sua página mais pesada do que deveria
Feche suas tags
Pode até parecer uma recomendação trivial. Porém tags não fechadas podem implicar em erros de interpretação.
Um erro destes pode fazer com que seu código fique mais complexo de ser interpretado, por forçar ao browser inferir mais decisões do que deveria. Isso acaba deixando o site mais lento podendo, na verdade, não carregar todo seu código HTML
Evite estruturas complexas
Na maioria das vezes o que fazemos pode ser simplificado com um simples refactoring. Pedir opiniões de colegas tende a ajudar nessa simplificação
As estruturas complexas tendem a deixar seu dom com mais nós do que deveria, tornando sua interpretação mais lenta do que deveria
Incorpore código
É possível incorporar códigos SVG, JavaScripts e CSS direto no HTML a fim de reduzir o número de requests e forçar a leitura/interpretaçao desse código mais rapidamente
CSS
O CSS é um recurso bloqueador de renderização
Isso implica que o browser não irá renderizar nada até o CSSOM estar pronto. Por causa disso, é importante entregar o CSS para o cliente o mais rápido possível
Isso pode ser feito através de incorporação de código crítico e/ou carregamento assíncrono (trazendo o que deve ser interpretado de cara síncronamente e os códigos não críticos assíncronamente)
O Critical Rendering Path sugere incorporar códigos críticos no HTML e trazer os demais de forma assíncrona
Use pré-processadores
A utilização de pré-processadores traz uma série de vantagens por viabilizar capabilities que o CSS não tem por padrão ou que ainda não é amplamente suportada (como variáveis, funções, herança etc)
A implementação correta dessas capabilities em um pré-processador como SASS, Less, Stylus ou qualuqer outro tende a tornar o código muito mais manutenível e otimizado (visto que estas ferramentas acabam por otimizar o código-fonte compilado)
Minifique seu código
Menos é mais, neste caso
Concatene seu código (!)
É um assunto meio polêmico. Pois a concatenação de código CSS acaba deixando o arquivo maior, porém, reduz a quantidade de folhas de estilo que serão requisitadas pelo browser
Deve-se ponderar sobre o que deve ser concatenado ou não
Evite seletores descendentes
Seletores descendentes tornam o processo de captura dos nós para estilização muito mais complexo para o browser, visto que regras do CSS são interpretadas da direita para a esquerda.
<ul>
<li>
<a href="#">Meu Link 1</a>
<a href="#">Meu Link 2</a>
</li>
</ul>
<style>
ul li a {
color: #F2AB09;
font-size: 10px;
}
</style>
<ul>
<li>
<a href="#" class="link-boladao">Meu Link 1</a>
<a href="#" class="link-boladao">Meu Link 2</a>
</li>
</ul>
<style>
.link-boladao {
color: #F2AB09;
font-size: 10px;
}
</style>
Bom
Ruim
Evite seletores universais
Seletores como *, [disabled], [type="text"], por exemplo, são bastante custosos para o browser por forçar a checagem de cada um dos elementos do DOM
Use, mas não abuse. Isso aqui não é C&A
Utilize propriedades custosas com moderação
Propriedades como border-radius, box-shadow, transform, filter e position:fixed são úteis, porém relativamente custosas para o browser. Pode utilizar, mas cuidado para não exagerar
Force renderização em GPU
Existe um "hack" que força a utilização da GPU ao invés da CPU para a renderização do CSS/HTML por implicar na utilização de três dimensões (3D). Isso acaba desonerando a CPU, tornando a renderização um pouco mais rápida
body {
transform: translateZ(0);
}
CSS carregado sob condições
Podem surgir situações em que você precise de uma folha de estilo para impressões, ou para telas de celulares (não por redimensionamento, mas realmente de um dispositivo móvel)
É possível condicionar a chamada destes CSS para quando ele realmente for necessário
<link href="style.css" rel="stylesheet">
<link href="print.css" rel="stylesheet" media="print">
<link href="print.css" rel="stylesheet" media=“handhelds">
<!-- Browser Specific for IE6 -->
<!–[if IE 6]>
<link rel=“stylesheet” type=“text/css” href=“ie6.css”>
<![endif]–>
Use shorthands
O processo de renderização pode ser bastante simplificado a partir da utilização de shorthands do CSS
.minha-classe {
margin-top: 10px;
margin-right: 8px;
margin-bottom: 6px;
margin-left: 10px;
}
.minha-classe {
margin: 10px 5px 6px 8px;
}
==
KISS
Keep It Simple, Stupid!
Mantenha seu código simples, utilize pré-processadores, utilize alguma metodologia de desenvolvimento como o BEM, OOCSS, SMACSS, DRY CSS etc
CUIDADO com CSS-Reset
A utilização de CSS-Reset é uma prática bastante comum para limpar estilos-padrão que os browsers estabelecem para páginas WEB
O grande problema é que pelo fato de serem extremamente genéricos, podem estar limpando elementos que não serão utilizados em seu site (como um <ol>), e isso gera um overhead desnecessário na renderização
Importe o CSS de forma assíncrona
É possível importar o CSS como um preload
<!-- Inicia como um preload e depois alterna para estilo -->
<link rel="preload" as="style" href="estilo.css" onload="this.rel='stylesheet'"/>
<!-- Fallback pro caso de JavaScript estar bloqueado -->
<noscript><link rel="stylesheet" href="estilo.css" type="text/css"/></noscript>
Utilize alguma solução de JavaScript para o caso do browser não suportar preloads
Use cache
A utilização de cache retira a necessidade de ficar sempre requisitando arquivos que não mudam com frequência (como o Bootstrap, por exemplo). Estes arquivos poderiam ser cacheados para que em futuros acessos o usuário não precisasse mais baixar aquele arquivo.
JavaScript
<3
Por poder atuar diretamente no DOM e em parte do CSSOM, o JavaScript é indiretamente um recurso bloqueador de renderização
As recomendações são extremamente parecidas com as do CSS:
- Disponibilize o JS o mais rapidamente possível
- Incorpore o código crítico
- Carregue de forma assíncrona o que não for crítico
Sempre que possível,
carregue seu código de forma assíncrona
Carregar o JavaScript de forma assíncrona atenua o impacto no carregamento do DOM. Você pode fazer isso através de bibliotecas específicas para isso como o RequireJS, SystemJS etc; ou requerendo-os a partir utilizando o atributo async
Também é possível carregar script após a construção do DOM e CSSOM usando defer, contudo eles serão carregados na ordem que forem inseridos
<script async src="meuscript.js"></script>
<script defer src="meuscript.js"></script>
<script defer src="meuscript2.js"></script>
Minifique seu código
A minificação, como dito anteriormente, torna seu código menor, fazendo com que o mesmo seja adquirido e interpretado mais rapidamente
Agora, tenha cuidado para o name-mangling não te causar problemas devido a mudança de nomes de variáveis.
Sempre re-teste seu código JavaScript após minificá-lo
Concatene seu código
A partir da concatenação você unifica todos (ou parte) de seus scripts em um único arquivo que, apesar de ser mais pesado que os demais, reduz a quantidade de requisições que o browser fará
Este ponto, especificamente, é discutível. Pois pode trazer códigos que não são críticos e que, consequentemente, não precisarão ser utilizados na hora
Coloque os scripts no final do HTML
O fato de você colocar os scripts no final do HTML reduz o tempo de renderização porque irá viabilizar o começo da construção do DOM e CSSOM mais rapidamente
PRPL - Push, Render, Pre-cache & Lazy-load
O PRPL é um padrão que otimiza o gerenciamento de códigos a partir de um massivo code-spliting e code-caching
Basicamente, ele pré-carrega alguns recursos que serão utilizados em outras views/rotas
Os Service Workers são muito utilizados para gerenciar o cacheamento e carregamento destes arquivos
Prefira, sempre que possível, animações em CSS ao invés de JavaScript
Como o CSS é pré-interpretado pelo browser, ele demanda menos processamento do que o JavaScript
Outra vantagem é que através das animações CSS você pode forçar a renderização em GPU ao invés de CPU
Faça uso de cache
A utilização de cache retira a necessidade de requerer arquivos que não mudam com frequência
Scripts de bibliotecas ou frameworks como JQuery, Angular, Vue e React, por exemplo, podem ser cacheados de forma que o usuário não precise requerer aquele arquivo novamente durante um período de tempo
Evite interações com DOM, Canvas etc
A interação com elementos do DOM, Canvas e afins tende a gerar uma degradação da performance por implicar em reflow e repaint
O Reflow é basicamente o recálculo do posicionamento, tamanho e outros atributos dos elementos que compõem o DOM
O Repaint é a reimpressão destes elementos em tela
Reavalie suas dependências
É comum que com o passar do tempo sejam adicionadas novas dependências enquanto algumas outras podem cair em desuso
Neste caso, sempre que deixar de utilizar uma dependência, lembre de removê-la do projeto, de forma a evitar processamento desnecessário
Cuidado com manipulação de eventos
Os eventos são uma ferramenta incrível. Porém, quando não utilizados corretamente, podem degradar sua aplicação
- Eventos de rápida sucessão, como o mousemove, por exemplo;
- Remover event listeners quando não precisar mais deles;
- Event Bubling
<html>
<body>
<div id="div">
<button id="botao">CLICA</button>
</div>
<script type="text/javascript">
document.querySelector("#botao").addEventListener('click', event => console.log('CLIQUE EM BOTAO'));
document.querySelector("#div").addEventListener('click', event => console.log('CLIQUE EM DIV'));
document.querySelector("body").addEventListener('click', event => console.log('CLIQUE EM BODY'));
</script>
</body>
</html>
Tente seguir a especificação
da Ecma Foundation
O EcmaScript (nome oficial do JavaScript) é mantido pela Ecma Foundation. Sua especificação para o JavaScript define os propósitos e usos gerais do JavaScript
Essas especificações são seguidas por quem desenvolve browsers (seja Google, Microsoft, Apple, Mozilla etc) para implementar a forma com que o JavaScript deve ser interpretado
Convém ressaltar que essas empresas não implementam, necessariamente, todas especificações do JavaScript. Podem haver funcionalidades que estão implementadas em um browser e no outro não, contudo, isso tem se tornado cada vez mais raro
Fontes
Evite utilizar fontes demais
Utilizar muitas fontes implica em um trabalho extra para o browser
Evite utilizar muitas variantes
Só mantenha em seu código as fontes e variantes (semibold, italic etc) que você realmente usa
Evite utilizar muitas variantes
Só mantenha em seu código as fontes e variantes (semibold, italic etc) que você realmente usa
Forneça os formatos otimizados para cada navegador
Cada um dos formatos (WOFF2, WOFF, EOT, TTF etc) tende a ser mais otimizado para um browser ou outro. Portanto, opte por disponibilizar estes formatos que o browser se encarregará de utilizar o mais apropriado
Caso você não tenha todos formatos, existem ferramentas online (como o Transfonter, por exemplo) que convertem a fonte que você disponibilizar para os demais formatos
Utilize cache
Como as fontes são um recurso que não tendem a ser alteradas ao longo do tempo, podem, sem sombra de dúvidas, ser cacheadas
Font Loading API
A utilização da Font Loading API pode otimizar seu Critical Rendering Path por viabilizar carregamento assíncrono de fontes, entre outras funcionalidades
Imagens
As imagens na maioria das vezes as grandes vilãs na performance de páginas web
Mas na maioria das vezes,
também são essenciais
Lazy-load
O carregamento assíncrono de imagens geralmente já melhora bastante a velocidade de renderização quando se tem muitas imagens
Não existe muita vantagem em carregar imagens do final do site, se o usuário ainda está no começo
Tente utilizar formatos otimizados
Imagens em formatos otimizados como .webp dão um ganho de performance na renderização destas imagens
Porém, muito cuidado com questões de performance!
Comprima imagens (por favor!)
Na maioria das vezes, as imagens tem metadados que não tem nenhuma utilidade para o browser (como o modelo da câmera que tirou a foto, por exemplo).
Além disso, dependendo do caso, também é possível reduzir a qualidade da mesma para diminuir o seu tamanho
Existem diversas ferramentas (como o TinyPNG e kraken.io, por exemplo) para realizar a compressão de imagens
Prefira estilizar diretamente na imagem
Se você tem uma imagem que vai servir apenas para ser um background com blur (por exemplo), opte por já disponibilizar a imagem com este estilo já aplicado ao invés de fazer isso via CSS
Agora, claro, só faça isso se a imagem sempre for ser utilizada daquela forma
Evite redimensionar no Front-end
Redimensionamento == Reflow / Repaint
Tente disponibilizar as imagens já no tamanho que elas devem se utilizadas. Por exemplo, se você vai usar uma imagem 200x200, pra quê você disponibilizaria uma imagem 1200x1200?
Tente utilizar sempre SVG ou font-icon para seus ícones
Dependendo da forma com que seja utilizado, o SVG já pode ser interpretado junto do HTML
Font-icons são melhores do que imagens por tratar as mesmas como fontes a serem utilizadas em elementos HTML
Cuidado com o tipo de imagem que usa
NUNCA use imagem
onde puder usar uma fonte
Se for utilizar muitas imagens pequenas, pense em utilizar um sprite
Repense se você realmente precisa daquela imagem
Outros
Se possível, use uma Content Delivery Network (CDN)
Transmita dados como GZIP
server {
...
//Configurações de GZIP no Servidor NGINX
gzip on;
//Somente o HTML é comprimido por padrão
gzip_types text/css application/javascript images/svg+xml;
}
Se possível, use HTTP2
- Remove restrição de 6 requisições por ter request multiplexada/assíncrona;
- Implementa Diff Request, omitindo headers que já foram enviados em outros momentos;
- Comprime Headers (Algoritmo HPACK)
- Implementa GZip por Padrão
- Reduz a necessidade de concatenação de arquivos
Cacheie tudo que puder
Utilize uma ferramenta de build para te auxiliar
Existem diversas opções como: Webpack, Grunt, Gulp, Browserify, Parcel e Yeoman
Cuidado com o Round Trip Time (RTT)
A latência da rede (RTT) implica na velocidade de transmissão dos bytes. Se possível, é bom optar por locais mais próximos dos prováveis usuários finais para reduzir o RTT.
Uma página web precisa de vários RTTs para funcionar. Desde o DNS Lookup (conversão da URL pra IP) até a HTTP Request
Após incorporar o código (SVG, CSS, JS etc), tente se manter no limite de 1460kb
Há uma especificação (RFC 6928) que indica aumentar o tamanho das janelas transmitidas pelo TCP para 10.
Cada janela tem 1460b, ou seja 1460b*10 = ~1460kb.
Isso implica em uma renderização mais rápida, visto que toda informação necessária é transmitida logo no começo (1 RTT)
Note que isso se refere ao tamanho transmitido, não ao tamanho real do arquivo necessariamente (ou seja, se você usa GZip, o arquivo pode ser um pouco maior do que 1460kb)
Utilize o resource hint preconnect para otimizar acesso a serviços externos
O resource-hint preconnect permite rodar em paralelo às requisições todo processo de DNS-Prefetch (busca do IP), SSL Handshake (tratamento de certificados de segurança, HTTPS) e abertura de conexão com o servidor.
Apesar de útil, é sugerível não exagerar nos preconnects para não afetar o processo das requisições.
<link rel="preconnect" href="//meu_video_do_youtube"/>
<link rel="preconnect" href="//meu_video_do_vimeo"/>
Utilize o resource-hint prefetch para pré-carregar e carregar informações
Utilizando o resource-hint prefetch é possível fazer o pré-download de arquivos e cachear os mesmos para uso futuro
É importante saber que o prefetch tem menos prioridade que as demais requisições. Portanto, seu uso é indicado para pré-carregar informações de páginas que ainda não estão sendo usadas mas que tem grande probabilidade de serem chamadas
<link rel="prefetch" href="script-da-proxima-pagina.js">
<link rel="prefetch" href="css-da-proxima-pagina.css">
Utilize o resource-hint preload para pré-carregar e carregar informações de alta prioridade
O resource-hint preload viabiliza o pré-carregamento de informações de alta prioridade.
É possível utilizar um atributo "as" informando para o browser com que prioridade ele deve baixar aquele resource.
É possível utilizar um atributo "type" para condicionar o download daquele resource ao suporte do navegador.
<link rel="preload" href="assets/styles/meuestilo.css" as="css">
<link rel="preload" href="assets/images/foto.jpg" as="image">
<link rel="preload" href="assets/fonts/OpenSans.woff2" as="font" type="font/woff2">
<link rel="preload" href="assets/fonts/OpenSans.ttf" as="font" type="font/ttf">
Utilize o resource-hint prerender para pré-renderizar páginas
O resource-hint prerender permite que o browser pré-renderize páginas HTML antes do usuário efetivamente abri-las.
Isso é feito em uma aba oculta. Quando o usuário vai efetivamente para a página, o navegador basicamente "transforma" essa aba oculta na aba principal.
Deve-se tomar cuidado com a pré-renderização para não sobrecarregar o browser do usuário. Além disso, indica-se também fazer a adição do pre-render dinamicamente via JS
<link rel="prerender" href="index.html">
Obrigado!
Depois me digam o que acharam
hugodeiro.com
/hugodeiro
/hdeiro
/hdeiro
hugodeiro@gmail.com
/hdeiro
Critical Rendering Path
By Hugo Deiró
Critical Rendering Path
Este slide aborda o Critical Rendering Path e recomendações de como otimizar páginas web.
- 453