5 dicas simples para deixar seu site mais performático
Introdução
Otimizar performance é parecido com economizar dinheiro.
Não adianta você economizar centavos apagando as luzes da casa, enquanto toma banho quente por horas.
Assim como economizar dinheiro, para otimizar performance é importante ter métricas para atacar a principal causa do problema, senão você vai estar jogando energia fora com algo que talvez não traga nenhum benefício perceptível.
A métrica também é importante para confirmar que nossa alteração realmente melhorou a performance.
No browser, você pode usar as diversas ferramentas que o DevTools oferece, como Performance, Performance Monitor, Memory, Network, etc, e também a extensão LightHouse.
Você também pode usar outras ferramentas como Elastic APM e similares para monitorar tanto o front-end quanto o back-end.
Nesse artigo eu não vou me basear em métricas, porque na realidade vou dar sugestões de coisas que você pode experimentar para melhorar performance.
A parte de medir e validar se houve melhoria de performance fica contigo.
1 - Use debounce e throttle
Debounce e throttle são funções para atrasar ou limitar quantas vezes um processamento é realizado.
O JavaScript não tem nativamente essas funções, porém você pode implementar ou usar uma biblioteca para isso.
Nesse exemplo aqui eu botei um event listener no evento de scroll
.
Eu fiz scroll por poucos segundos e esse listener foi disparado 240 vezes.
Se eu tiver alguma função que faz algum processamento pesado no meu event listener, eu vou travar a aba do navegador disparando esse processamento dezenas de vezes.
Debounce
O debounce
atrasa a execução de uma função por um tempo determinado pelo usuário.
Por exemplo:
window.onscroll = debounce(() => console.log('scrolled'), 1000)
O console.log
só vai ser executado 1 segundo depois do último evento de scroll.
Throttle
O throttle
executa a chamada para a função num intervalo definido pelo usuário.
window.onscroll = throttle(() => console.log('scrolled'), 1000)
Nesse caso, se o usuário ficar fazendo scroll, a cada segundo vai ter um console.log
.
2 - Coloque só o crítico no head
Se você coloca todos os seus JS e CSS no head
do seu index.html, você está fazendo com que sua página fique em branco até que todos esses arquivos carreguem.
Esse é o momento que vários usuários desistem de usar seu site!
<!DOCTYPE html>
<html lang="pt">
<title>Meu site lento</title>
<link rel="stylesheet" href="estilo_critico.css">
<link rel="stylesheet" href="estilo_nao_critico1.css">
<link rel="stylesheet" href="estilo_nao_critico2.css">
<script src="script_nao_critico1.js"></script>
<script src="script_nao_critico2.js"></script>
</head>
<body>
<!-- só vai aparecer depois que todos os arquvios forem baixados e processados -->
<h1>Bem vindo!</h1>
</body>
</html>
Uma regra simples é a seguinte:
só coloque no
head
o que você realmente precisa que seja carregado e processado antes do browser mostrar algo na tela.
Só coloque estilos no head
que você considere críticos, exemplo: fontes, layout básico, cores.
Só coloque scripts no head
se você realmente precisa que eles façam algo antes da página ser renderizada.
Se você não precisa que um estilo ou script execute antes da página ser renderizada, coloque no fim do body
.
Resultado do html anterior:
<!DOCTYPE html>
<html lang="pt">
<title>Meu site otimizado</title>
<link rel="stylesheet" href="estilo_critico.css">
</head>
<body>
<!-- vai aparecer assim que o estilo_crítico.css carregar -->
<h1>Bem vindo!</h1>
<link rel="stylesheet" href="estilo_nao_critico1.css">
<link rel="stylesheet" href="estilo_nao_critico2.css">
<script src="script_nao_critico1.js"></script>
<script src="script_nao_critico2.js"></script>
</body>
</html>
Obs.: no caso de scripts, você até pode deixá-los no
head
, desde que use defer
Saiba mais:
Critical rendering path - Web performance | MDN
3 - Coloque o crítico inline
Fazer uma requisição HTTP é algo que pode ser bem lento, principalmente em redes móveis, porque o tempo da requisição chegar no servidor e voltar é muito grande.
Dessa forma, no primeiro carregamento da sua página você deve buscar fornecer o crítico num único arquivo HTML.
<html lang="pt">
<title>Meu site rapidão</title>
</head>
<body>
<style>
/* meu estilo crítico */
body {
color: red;
}
</style>
<!-- vai aparecer mais rápido pois não vai fazer uma req. pro estilo crítico -->
<h1>Bem vindo!</h1>
<link rel="stylesheet" href="estilo_nao_critico1.css">
<link rel="stylesheet" href="estilo_nao_critico2.css">
<script src="script_nao_critico1.js"></script>
<script src="script_nao_critico2.js"></script>
</body>
</html>
Um site que faz isso bem é o G1:
Mesmo desativando o cache e simulando um 3G lento, em menos de 2 segundos o texto da notícia e o layout básico da página foi carregado, o que para um usuário é bem aceitável.
Já o Github é outra história:
O site carrega 8 estilos no head
.
O navegador levou 19 segundos para mostrar algo na tela! Muitos usuários desistiriam no caminho.
Também é preciso ter um cuidado especial com single page applications, pois elas são todas renderizadas no cliente, e muitas vezes acabam fazendo muitas requisições.
Você pode experimentar reduzir o número de requisições usando GraphQL ou outras técnicas como server side rendering.
Saiba mais:
4 - Use cache de requisições HTTP
Caching é uma ótima estratégia para diminuir tempo de carregamento de páginas e entregar uma experiência instantânea para o usuário.
4.1 - Cache-Control
e max-age
O cache-control
é um header de resposta HTTP que, entre outras coisas, diz para o navegador por quanto tempo ele pode salvar o resultado de uma requisição para usar no futuro.
Esse tempo é definido pelo max-age
, que é um valor sempre em segundos.
Exemplo: cache de dois minutos
Cache-Control: max-age=120
Se eu fizer umas requisições com o mesmo path, exemplo /img/1234.png
, a primeira vai trazer do back-end e a salvar em cache por 2 minutos.
Se na segunda vez ainda não tiver passado 2 os minutos, o resultado da requisição anterior vai ser reutilizado, sem nem precisar falar com o back-end.
Isso traz um benefício de performance enorme, porque trazer do disco pode ser milhares de vezes mais rápido do que trazer do servidor.
4.2 - ETag
ETag é um outro header de resposta que também serve como validação de cache.
O ETag é pensado para ser um código curto identificador daquela resposta.
Em resumo:
- respostas idênticas devem ter o mesmo ETag
- respostas distintas devem ter ETags diferentes
O motivo disso é que quando o navegador fizer uma requisição que responda com um ETag, ele vai salvar a resposta no disco e nas próximas vezes ele vai enviar o ETag para o servidor, falando "Se o body mudar, me retorna o novo body. Se não, me retorne um status 304 (Not Modified) sem body.".
ETags podem ser implementadas no lado do servidor fazendo hash do body da resposta.
Se for uma requisição para um arquivo, pode ser baseada no momento de última modificação.
Se for uma requisição que consulta dados do banco, também pode usar a última vez que um dado foi alterado...
Use sua imaginação.
4.3 - Combinando max-age
com ETag
Quem disse que você precisa escolher um ou outro?
Você pode perfeitamente pôr um cache de 5 minutos numa imagem, e também usar uma ETag.
Quando o cache expirar, o navegador vai fazer uma requisição checando se a ETag mudou, e se não, vai manter o dado que ele já tem em cache por mais 5 minutos.
Saiba mais:
5 - Mostre menos coisa na tela de uma vez
Mais elementos na tela significa mais coisa em memória, mais elementos para recalcular o estilo e layout, mais event listeners, mais trabalho para a GPU, etc…
Se tiver muitas imagens, coloque lazy loading, porque carregar muitas imagens em paralelo vão dar uma sensação de maior lentidão e pode até travar o seu site.
Use paginação e/ou infinite scroll para controlar a quantidade de conteúdo na tela.
Dessa forma o usuário não precisa carregar megabytes de dados de uma vez só para ver o início do conteúdo.
Conclusão
Como o título avisou, foram dicas bem simples, porém que eu considero bem úteis e eficazes no desafio de entregar boa performance.
Eu tenho vontade de falar sobre outros assuntos mais desafiadores, como memory leaks, repaint and reflow, e estou lendo o livro High Performance Browser Networking do Ilya Grigorik para transformar numa série de artigos também.
Por hoje é só. Aguardo seu feedback!