Executando verificação de segurança...
30

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.

Ferramenta Performance do DevTools

Você também pode usar outras ferramentas como Elastic APM e similares para monitorar tanto o front-end quanto o back-end.

Elastic APM

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.

window.onscroll = () => console.log('scroll')

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:
G1 parcialmente carregado

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.

First Contentful Paint do Github

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:

Inlining critical CSS

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:

Cache-Control - HTTP | MDN

ETag - HTTP | MDN

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.

Lazy loading de imagens

Use paginação e/ou infinite scroll para controlar a quantidade de conteúdo na tela.

Paginação

Infinite scroll

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!

Carregando publicação patrocinada...
2
2

Ótimo artigo, acho importante mencionar esse vídeo do Fábio Akita que fala sobre isso.

Ele fala sobre Pool de Conexões, Load Balancer, Caching, Jobs Assíncronos, CDN'S e outras coisas bem importantes nesse assunto.

2
1
1
1
1
1

Gostei do artigo! Vou salvar aqui pra acrescentar nos meus estudos.

Achei interessante o exemplo do G1 e a diferença enorme com o GitHub.

Algo simples também, mas importante, é usar as imagens nos tamanhos e formatos adequados, não só uma imagem gigante e ir alterando no css.

[VELOCIDADE DO SITE]

1
1
1

Cara so o lazyloading ai ja da uma diferenca enorme, um abismo, se o seu site conter imagens de produtos, gifs ou icones. Excelente artigo, basicamente um tutorial de como fazer seu site nao demorar milenios (mais de que 5 segundos) pra carregar haha.