TL;TR

Eu fiz os seguintes passos para otimizar a performance do meu blog:

  • Minifiquei dos css
  • Removi o jquery
  • Otimizei imagens para retina display.
  • Minifiquei o html

Resultado:

Utilizei como benchmark o post que falo como fazer hello world usando golang:

  • De duas requisições de css para uma
  • De 13,7 kb de tamanho de css para 10,7kb
  • Para o caso de uso de js, de 46,3kb para 11,4kb.
  • Redução de 12% no tamanho do markup

Trabalhos futuros

Melhorar o html e o css.

Discussão

Quando eu voltei a blogar, acho que esta é minha quarta tentativa, decidi que a estrutura necessária para o blog seria a mais simples possível. Não queria gastar dinheiro com hospedagem do blog, me preocupar com software, atualizações e banco de dados. Queria, e ainda quero, é apenas focar em escrever.

Como alguns amigos na globo estavam usando o Jekyll e github pages, decidi avaliar estas opções.

O Jekyll é um gerador de páginas estáticas escrito em ruby. Ou seja, você escreve na sua máquina um post em markdown ou rst e o Jekyll trata de gerar um html completo do seu blog. Portanto, não preciso de um software rodando na internet e nem suas dependências. Tudo estático. Mas ainda é necessário servir para a internet estes htmls. Ai entra o github pages.

O github pages olha para um repositório no github e usando uma estrutura de arquivos e pastas, bem acoplada ao que o jekyll depende, gera o html final, tal qual o jekyll. Assim, eu consigo manter meus posts num repositório na internet, salvo de problemas eventuais na minha máquina e ainda sirvo meu blog. E o melhor, sem custos financeiros.

Mas ainda é necessário investir tempo na escolha de um tema bacana para o blog, observar se é responsivo e ficar de olho nas métricas de performance e SEO.

Eu escolhi o Cactus como tema. Minimalista, responsivo, já integrado com pygments - para coloração de sintaxe de linguagens de programação-. Tudo que precisava.

Depois que botei o blog no ar e alguns posts - para dar uma animada - passei a olhar a performance de carregamento do site. E anotei alguns pontos chaves:

  • Css não era minificado
  • Js não minificado
  • Html não minificado

Do ponto de vista de backend, não há o que otimizar. O site já está na forma ótima para internet e como uso o github pages não tenho acesso a nenhuma configuração em termos de máquinas e servidores.

CSS

Como trabalho com web sei que existem algumas regras básicas ao lidar com carregamento de css:

  • O css deve ficar no topo da página.
  • Css minificado
  • Css comprimido

obs.: Foge do escopo deste post performance do css em si. Trato apenas de carregamento

Ao analisar o tema que escolhi tinha para css vi que:

  • O css já estava no topo da página. - OK
  • dois arquivos de css sendo carregados - Não tão OK
  • não estavam minificados - NOK

O primeiro passo foi minificar o CSS. Minificar é um processamento do arquivo para remover todos os espaços em brancos e caracteres, como quebra de linha (\n), não necessários. A primeira abordagem que pensei foi copiar e colar numa destes sites que fazem a minificação. Mas e se eu precisar modificar o css? Vai ser um inferno. Assim, também resolvi atacar outro problema, não relacionado a performance de load, mas de gerência de código. Criei a estrutura de scss para o meu projeto.

Assim, antes tinha:

| - assets/
   |- css/
    |- style.css
    |- highlight.css

O arquivo style.css tem 12,3kb e o highlight tem 1,4k. O maior problema aqui não é tão o tamanho dos arquivos, mas a necessidade de fazer duas requisições. Além do que, quando o browser encontra a diretiva de carregamento do css ele para o carregamento da página até tê-lo no contexto.

Então, tenho que ter apenas um arquivo! Poderia até por inline no html. Mas isto não é uma boa prática.

Agora tenho: :::shellscript | - assets/ |- css/ |- estilo.scss

Dentro do arquivo estilo.scss

@import "style";
@import "highlight";

Os @imports são diretivas do scss para incluir outros scss, que são exatamente os arquivos style.css e highlight.css. No momento eu só fiz renomear a extensão de .css para .scss e mover para outra pasta.

Já as duas linhas com "---" no início do arquivo são necessárias para informar ao jekyll que o arquivo estilo.scss deve ser processada como um arquivo final, que vai entrar na estrutura final do blog. O legal é que o jekyll já suporta nativamente o sass.

Logo, a nova estrutura de diretórios ficou:

{% highlight bash %} |- _sass/ |- _style.scss |- _highlight.scss

O jekyll irá compilar os arquivos scss num único arquivo estilo.css. Mas ainda tenho que mudar no html para o novo arquivo de estilos:

De: {% highlight html %} {% endhighlight %} Para: {% highlight html %}

Resolvido o problema de ter dois arquivos de css. Ainda faltava o problema da minificação do arquivo.

Com jekyll foi extremamente simples de resolver. Bastou adicionar no _config.yml o bloco:

{% highlight yaml %} sass: style: :compressed

Que reduziu o arquivo de 13,4kb para 10,7kb, aproximadamente 20%.

Nada mal! De dois arquivos para apenas um, com redução de 20%! Isto pode não parecer muito. Mas a técnica usada é escalável e é usada por exemplo no globoesporte.com, portal para o qual trabalho.

Javascript

Também podemos aplicar a técnica de minificar css para arquivos js. Mas decidi analisar primeiro o js.

A primeira coisa que estranhei foi o carregamento do jquery. Ora, meu site não tem nada de especial em termos de js, porque preciso do jquery?

Analisando todos os elementos de js da página, o único lugar que precisava do jquery era um js para mudar as imagens para quando a tela do dispositivo que acessasse o meu blog fosse retina. huuuuum...

Quais os impactos desta abordagem:

  • Carregar duas vezes uma imagem de valor semântico igual
  • Se o javascript der erro ou estiver desabilitado, o efeito é nulo
  • Carregar o jquery apenas para substituição de imagem? WTF!

Existem outra técnicas para assegurar que a imagem será a melhor no retina. A que eu escolhi foi ofertar imagens com as dimensões sendo o dobro da necessidade. Exemplo, meu avatar na home precisa de uma imagem 80x80, então vou ofertar uma imagem de 160x160. Você pode está se afirmando: "opa, ele está aumentando o peso da página sem necessidade". Mas na computação tudo é uma questão de medir a relação custo benefício. Eu sei que para uma pessoa nova entrando no meu site ela irá pagar o custo desta imagem maior, mas a tendência a longo prazo que a imagem estará cacheada no browser (já que esta imagem não será mudada com tanta frequência e ela é transversal ao blog). E esta abordagem é melhor que usar um js, que demandará toda vez que o usuário entrar na página executa-lo.

Antes deste post eu tinha duas imagens para o avatar com densidades diferentes. Uma de 80x80 com 3,8Kb e outra de 160x160 com 12,4kb. Ou seja, caso o js detectasse que era uma tela retina, só para mostrar o meu avatar seria necessário baixar 16,8kb só de imagem, além do jquery: 29,3kb; e do outro js de processamento: 0,7kb. Totalizando 46,3kb. Usando a abordagem de uma imagem dimensionada baixo apenas 11,4kb para qualquer caso!

Ainda tive mais uma oportunidade de performance que foi por o js de highlight em modo defer. Outros js que preciso, são o do google analytics e do disqus que já estão em sua forma recomendada.

HTML

Apesar do github pages entregar todos os seu conteúdos usando gzip, ainda é possível ganhar alguns bytes minificando o html.

O que eu utilizei foi o jekyll-compress para minificar html usando jekyll e o github pages. Há outras ferramentas para realizar esta tarefa, mas o github pages tem um conjunto limitado de plugins disponíveis que não inclui um para minificar html.

O primeiro passo e configurar o _config.yml para ter o bloco compress_html:

compress_html:
  clippings: all
  comments: all
  endings: []
  profile: false

profile indica o resultado da compressão. Pode deixar false. Caso queria ver as estatísticas, deixe true o valor que aparecerá uma tabela ao final da página.

endings, remove todas as tags opcionais de fechamento. Por exemplo, é opcional ao abri "\<p>" por o "\</p>". Eu não gosto desta abordagem, por isso o valor []. Caso você queira remover todas, use all. ou preencha a lista das tags que você deseja remover.

comments, remove todos os comentários. Qual seria o caso de um valor diferente de all? Por exemplo os bloco de comentários condicionais, como o "<!--[if gte IE 7]>" . Caso você não queira remover este tipo de bloco, mas remover os comentários regulares "<!-- isto é um comentário regular --!>" você usa a valor para comments como: ["<!--", "--!>"].

clippings, são as tags que podem ter seus espaços em brancos removidos, já considerando o caso da tag pre. Caso, você queira ser explicito, enumere uma a uma dentro da lista.

Por fim, é necessário preparar o html. É necessário usar o arquivo compress.html do jekyll compress e adiciona-lo a pasta _layout:

{% highlight bash %} |- _layout/ |- compress.html |- default.html |- post.html

E mudar o arquivo default para conter o seguinte cabeçalho:

{% highlight bash %}

layout: compress

{% endhighlight %}

O jekyll compress usa a própria linguagem de template liquid para minificar o html.

O resultado que obtive para a página hello world usando golang foi uma redução de 9kb para 7,9kb. 12% de melhoria (sem o gzip).

Novamente, minificar html é uma técnica que escala, pode ser usada em grandes portais e num contexto de tráfico de dados intenso pode resultar em economia de dinheiro.

Conclusão

Para a página hello world usando golang todo o esforço, sem considerar o disqus e o google analytics, resultou em:

  • Três requisições a menos
  • Melhora na renderização da página com menos elementos bloqueantes
  • Redução de 360kb para 330Kb, 8% de melhora global para o post.

De modo geral, o trabalho de otimização foi bom e a escolha pela dupla jekyll + github pages foi boa.

O próximo passo é tentar melhorar o css e o markup e fontes que vieram com o tema que escolhi.