Otimização Extrema: Maximizando o uso de CSS Sprites

Começando a série de otimização extrema de seu site ou aplicação, iremos caminhar inicialmente pela otimização de itens relacionados ao front-end. Temos que ter um conhecimento inicial sobre como os browsers funcionam quando relacionado ao paralelismo de itens que são buscados do servidor.

Por padrão, os browser do mercado atualmente buscam paralelamente no máximo 8 ítens, incluindo páginas (x)html, folhas de estilo, arquivos javascript e imagens. Levando isso em consideração, quanto maior a quantidade de itens a serem carregados, maior será o tempo gasto para o carregamento da página. Óbvio, não é?

Em alguns browsers, o acesso a informação de quantas requisições simultaneas são feitas pelo browser é feita de modo tranquilo, como por exemplo no firefox. Entrando nas configurações do browser, digitando about:config na barra de endereços, e confirmando a mensagem de que você sabe o que está fazendo, procure pela diretiva network.http.max-persistent-connections-per-server, e claramente você pode ver que o ítem está com o valor seis, mostrando que o browser só vai buscar seis itens por vez. Você, é claro, pode mudar esse valor, mudando para quaisquer que seja o valor de itens que você queira que o browser busque por vêz. É claro que isso só vai mudar pra você, e não para todos os usuários que utilizarem seu serviço.

network.http.max-persistent-connections-per-server

Os servidores http em geral, tem diversas rotinas que farão automaticamente o tratamento das requisições, com a proposta de otimizar seus serviços, economizando recursos do servidor onde ele está alocado. Mas eles não são tão inteligentes assim. É seu trabalho como programador front-end, disponibilizar seus documentos de forma que o browser e o servidor trabalhem de forma otimizada.

Nossos próximos passos serão para diminuir ao máximo a quantidade de requisições que o browser vai fazer ao servidor. Se o que você está trabalhando tende a ter bastante acesso, vale muito a pena diminuir o número de requisições HTTP que serão feitas.

minha-lista-coisas

O html gerado na imagem acima, é o objeto dos nossos testes. Montei um pequeno documento linkando um documento css, relacionando três imagens a três classes distintas. O documento em questão ilustra uma situação normal em interfaces, onde um item tem ações diversas relacionadas, e essas ações estão relacionadas semióticamente com uma imagem.

Aqui vamos começar nossa nossa primeira otimização no documento css, iremos minimizá-lo removendo tudo o que é desnecessário para o browser, como por exemplo, espaços. Para tal, utilizaremos a ferramenta yuicompressor. Como você pode ver abaixo, o código fica um pouco ilegível para nós, mas para o browser continua sendo o mesmo documento.

Fazer isso pode se tornar uma tarefa bem chata, ainda mais se você tem uma grande quantidade de arquivos CSS(o que desencorajo, em um post mais a frente vou falar disso), então se o projeto no qual você está trabalhando possui integração contínua, e como servidor o jenkins, não custa nada configurá-lo para fazer esse trabalho para você. A tarefa que o ant deve executar pode ser algo como o código que você digita no seu prompt de comando:

Como você pode ver na imagem abaixo, em uma página simples, composto de um documento htmt, uma folha de estilo e três imagens geraram cinco requisições HTTP, onde 4.23KB foram transferidos e houve 26ms para os elementos serem carregados na página.

É pouco, se você levar em consideração as conexões atuais, e claro que o exemplo é apenas uma situação que quase não existe, uma página com 5 registros e somente esses estilos. Em aplicações e sites de verdade podemos multiplicar sem nenhum medo esse número por 100. Olhando mais intrinsecamente para esse exemplo, com relação ao custo dessa página para a empresa que o servirá, a quantidade de tráfego que essa página gera é descomunal(!) se você multiplicar por 10000 usuários, e 100000, 1000000!

Ok ok! Exagerei um pouco! Já que é uma página simples, mas sempre pense que a sua aplicação é feita de diversos documentos sendo carregados, e tudo o que você puder fazer para tornar a experiência do usuário mais ágil, é um ganho para alguém!

sprite-without-sprite

Ok! Vou comprimir meus arquivos CSS. E o tal dos CSS Sprites?

Se você trabalha com front-end e nunca ouviu falar dessa técnica definitivamente você tem problemas em suas aplicações! A técnica consiste em juntar várias imagens em apenas uma, assim diminuindo o número de requisições http feitas para o servidor. O próximo passo é mapear a posição de cada item dentro do arquivo CSS. Para gerar o sprite, utilizei um serviço muito interessante chamado spritegen. Testei o alinhamento horizontal e vertical na tentativa de conseguir algum ganho em relação ao tamanho da imagem, mas permaneceu o mesmo.

sprite

Abaixo, mapeamos nossa imagem como mencionado.

Como já aprender como compactar o código css, vamos aplicar o compressor nesse aqui também!

A imagem abaixo ilustra claramente nosso ganho. Além de agora termos carregado somente 2.69KB(quase metade do tamanho anterior), nossa página levou apenas 13ms para ser carregada. Metade do tempo que levava para ser carregada anteriormente. Além disso, Diminuímos de cinco requisições para apenas 3!

sprite-with-sprite

Ok! Entendi! Mas e temos como melhorar isso? SIM!

Há alguns posts atrás, mostrei uma técnica onde usamos a codificação base64 para diminuir o número de requisições http feitas ao servidor, e agora vamos aplicar a mesma técnica em nosso sprite, incorporando a imagem ao documento CSS.

Utilizei o código que está anexado ao post mencionado para gerar a imagem codificada e o código abaixo quando anexado ao documento CSS.

Vamos comprimir esse código também, para obtermos ainda mais performance da nossa aplicação.

Olhando a imagem abaixo de relance não vemos muitas diferenças, mas muita coisa mudou se relacionado aos exemplos anteriores! Conseguimos diminuir ainda mais o tamanho de nossa tranferência. Agora temos somente 2.34KB! Se você é um grande observador vai ver que o tempo continua inalterado, ainda em 13ms. Porém existe uma diferença aí. O tempo de load foi menor. Apesar da ferramenta que utilizei dizer que houve três requisições, apenas duas foram feitas. Como a imagem está dentro do documento CSS não há necessidade do browser fazer uma terceira chamada, isso pode ser observado pelo recurso gráfico que a ferramenta me fornece. Se você obsevar atentamente, na requisição os documentos localhost(azul), css.css(verde) possuem um rastro de cor clara que relaciona o tempo que o browser fez a chamada ao servidor até a entrega. Agora observe a terceira chamada(roxo). Não possui rastro, por que não houve nenhuma requisição feita para o browser. Não era necessário, a imagem estava codificada dentro do arquivo de CSS que já havia previamente sido carregada.

sprite-sprite-base64

Com o que fizemos do início até agora, conseguimos além de diminuir o tempo de carregamento da página, o tamanho final somando todas os ítens que foram requisitados e além disso o número de requisições. Passamos de cinco para apenas duas.

Essa técnica pode ser bem trabalhosa inicialmente, mas pode gerar ganhos inacreditáveis se aplicados a interfaces onde existem muitos itens semióticos. Observando ainda um pouco mais, seu documento CSS pode crescer de tamanho, porém, se aplicadas as técnicas de compressão do arquivo como visto nesse artigo, você pode até diminuir seu tamanho comparando a soma do arquivo css mais os diversos arquivos de imagem que podem existir em sua aplicação.

Existe ainda um outro artifício para ser aplicado para tornar essa técnica ainda mais eficaz! O controle quase nazista do cache dos arquivos de sua aplicação. Mas isso é assunto para outro artigo!