Tutorial MongoDB para iniciantes em NoSQL – Parte 5

MongoDB Logo
MongoDB Logo

E chegamos ao quinto artigo da minha série de tutoriais de MongoDB para iniciantes em NoSQL. Caso esteja caindo de pára-quedas nesta série, seguem os links dos posts e seus respectivos assuntos:

Neste quinto artigo tratarei de como manipular documentos que possuam subdocumentos e campos multivalorados, usando como base o banco de blog que modelamos no artigo anterior.

CRUD com Subdocumentos

No segundo artigo desta série nós vimos como buscar, inserir, atualizar e excluir documentos em coleções MongoDB. No entanto, sempre com documentos planos, sem níveis, a forma mais básica de armazenar dados.

Mas e quando temos um subdocumento dentro de outro documento, assim como o autor dentro de um artigo de blog?

Vamos começar pelo C do CRUD, que no caso do MongoDB é representado pelo método insert. O comando abaixo insere um novo artigo incluindo o subdocumento ‘autor’:

Note que eu não passo o _id do artigo pois ele é autoincremental e controlado pelo próprio MongoDB. Já no caso do autor, ele pertence à outra coleção, a de autores, e o _id que está junto dele deve ser o mesmo do autor original na sua referida coleção. Imagina-se que em uma aplicação que salve um artigo, que a informação do autor do artigo esteja em sessão ou em um componente de tela para ser passada ao MongoDB corretamente.

Falando do R do CRUD, o find no MongoDB, podemos facilmente usar campos de subdocumentos como filtros em nossas consultas, como segue:

Essa consulta retorna todos os artigos cujo nome do autor seja literalmente Luiz. Qualquer filtro existente pode ser usado aqui, sobre qualquer campo do subdocumento autor, mas atenção à forma que referenciei o campo, usando o nome do subdocumento, seguido de um ‘.’, e depois o nome do campo. Repare também que neste caso o uso de aspas ao redor da expressão é obrigatório.

Seguindo com o U do CRUD, vale tudo o que vimos até agora. Para substituir documentos que possuam subdocumentos usando o comando update, você tem de passar eles também, para não se perderem na atualização, como abaixo:

Se for usar um campo de um subdocumento no filtro do update, valem as mesmas regras do filtro do find que expliquei anteriormente. O mesmo vale caso queira aplicar algum update operator em um campo de um subdocumento. Quer um exemplo prático?

Na modelagem de blog que fizemos no post anterior, replicamos o nome e _id do autor em todos os posts escritos por ele. Mas o que acontece caso o nome do autor seja alterado no documento original dele, que fica na coleção de autores?

Teremos de replicar esta alteração em todos os artigos que tenham sido escritos por aquele autor, como abaixo. Neste exemplo, suponha que o autor Luiz teve o nome alterado para Luiz Fernando, então temos de atualizar todos os posts escritos por ele. Como é somente esta pequena informação que mudou, usaremos o update-operator $set, para não ter de sobrescrever os documentos inteiros.

Para um update mais preciso, eu poderia substituir o filtro autor.nome por autor._id, considerando que nomes de autores podem ser repetir em um mesmo blog.

Com os update-operators $set, $unset e $rename é possível manipular os campos dos subdocumentos também, da mesma forma que faria com o documento principal, apenas usando a notação “subdocumento.campo”.

Finalizando o CRUD com subdocumentos, o D de delete é realizado usando as mesmas regras de filtro do find, caso queira excluir todos os documentos que possuam um valor específico em um campo de um subdocumento. Sem mistério algum.


CRUD com campos multivalorados

Outra preocupação é com a manipulação de elementos em campos multi-valorados, algo inexistente em bancos relacionais que sempre assumem relações 1-N ou N-N nestes casos. Salvo gambiarras que já vi por aí de salvar strings CSV ou JSON em coluna de registro e outras loucuras que sempre acabam gerando dor de cabeça pois não são pesquisáveis.

Começando pelo C do CRUD, o insert do MongoDB funciona de maneira muito óbvia para campos multivalorados: apenas passe null para nenhum valor ou o array (entre colchetes) de valores iniciais daquele elemento (sim, o MongoDB permite que depois você adicione ou remova elementos).

Se você procurar no exemplo anterior de insert, verá que passei null no campo multivalorado de tags do artigo. A outra opção, passando valores iniciais, segue abaixo:

Neste caso o campo multivalorado tags é um array de strings. Caso deseje, você pode inserir um documento que possua campos multivalorados de documentos também, como no caso das categorias que modelamos no post anterior:

Mas é quando entramos no R do CRUD com campos multivalorados em MongoDB que começamos a explorar um novo universo de possibilidades e novos filter-operators da função find.

Considere que temos três artigos salvos na nossa base MongoDB (omitirei os campos que não nos importam no momento):

Para fazer buscas usando campos multivalorados como filtro é muito simples: você deve usar os operadores de filtro $all e $in. Exemplo de consulta por todos os artigos que possuam todas (ALL) as seguintes tags NodeJs e MongoDB :

O operador $all vai garantir que só sejam retornados artigos que possuam no mínimo as duas tags informadas (somente o Artigo 1), equivalente à consulta SQL abaixo:

Agora um exemplo de consulta por todos os artigos que possuam uma (IN) das seguintes tags NodeJs ou MongoDB:

Neste caso todos os três artigos serão retornados, assim como no equivalente SQL abaixo (coloquei reticências por preguiça de preencher todas as colunas):

Mas agora se quisermos apenas os artigos que possuam ao menos uma categoria (o campo de categorias é multivalorado, lembra?) com o nome Desenvolvimento?

Quando aplicamos um filtro sobre um campo que é multivalorado o MongoDB entende que podemos passar um novo filtro a seguir, que será aplicado aos campos do subdocumento. Por exemplo:

Vai retornar todos os documentos que tenham ao menos uma categoria cujo nome seja ‘Desenvolvimento’. Também poderia usar filter-operators se fosse necessário como $gte, $regex, etc.

Avançando para o U do CRUD, no update de campos multivalorados também temos diversas opções a serem exploradas pois é no update que adicionamos e removemos elementos do nosso array, quando quisermos fazê-lo. Para tais tarefas deve-se usar os operadores $pull (remove o elemento do array) e $push (insere o elemento no array), sempre dentro de um comando de update, como segundo parâmetro (update-operator). Ex:

Pode parecer um pouco estranho no início, mas na verdade é muito mais simples, pois é a mesma ideia que já fazemos há anos com coleções em linguagens de programação, onde geralmente temos métodos como add e remove ou assemelhados.

Finalizando, o D do CRUD, em campos multivalorados funciona da mesma forma que o find, considerando que no deleteOne e deleteMany do MongoDB também passamos um filtro por parâmetro.

Quer aprender outras dicas? Leia o que escrevi sobre boas práticas com MongoDB no blog da Umbler!

Curtiu o post? Que tal aprender a usar MongoDB com Node.js? Então clica no banner abaixo e dá uma conferida no meu livro sobre programação web com Node.js!

Como aumentar o desempenho de seu site – Épico!

Atualizado em 04/11/2017!

O artigo de hoje traz inúmeras dicas de como aumentar a performance dos seus sites, independentes de qual plataforma eles executam ou em qual linguagem eles são desenvolvidos. No final das contas tudo vira HTML e a comunicação é feita via protocolo HTTP.

Existem muito mais dicas do que as apresentadas aqui, listei apenas as de uso mais comum e que se aplicam à maioria dos sites. Muitas destas dicas somente fazem efeito quando seu site possui um número expressivo de acessos, então não espere que seu blog desconhecido mostre sinais de super desempenho ao utilizar quaisquer das dicas abaixo.

Por que você deveria se preocupar com a velocidade do seu site? Dê uma lida nesse post onde apresento ótimos argumentos.

As dicas estão divididas em categorias:

  1. Reduza as requisições HTTP
  2. Utilize os cabeçalhos Expires e Cache-Control
  3. Componentes GZip
  4. Folhas de estilo no início
  5. Scripts no Final
  6. Use CSS e JavaScript externos
  7. Minimize JavaScript e CSS
  8. Carregue componentes após o load
  9. Carregue componentes antes do load
  10. Divida componentes entre domínios
  11. Fora 404!
  12. Otimize as imagens
  13. Otimize seus CSS Sprites
  14. Use um CDN
  15. Use as versões mais recentes das tecnologias do seu site

Vamos lá!

#1 – Reduza as requisições HTTP

80% do tempo de resposta ao usuário final é gasto na apresentação do site (front-end). A maioria deste tempo é perdido baixando todos os componentes na página: imagens, folhas de estilo, scripts, Flash, etc. Reduzindo o número de componentes reduz por sua vez o número de requisições HTTP necessárias para renderizar a página. Esta é a chave para páginas mais velozes.

Uma vez que você reduza o número de componentes na página você estará simplificando o projeto da mesma. Mas existe alguma maneira de criar páginas com conteúdo rico enquanto mantém tempos curtos de resposta? Existem algumas técnicas para reduzir o número de requisições HTTP, enquanto se preserva os projetos de páginas ricas.

Arquivos combinados são uma maneira de reduzir o número de requisições HTTP combinando todos os scripts em um único arquivo de scripts, e similarmente combinando todos os CSS em uma única folha de estilos. Combinar arquivos é bem desafiador quando os scripts e folhas de estilo variam de página para página, mas fazê-lo antes de lançar seu site vai melhorar o tempo de resposta das páginas.

CSS Sprites são o método preferido para reduzir o número de imagens requisitadas. Combine suas imagens de fundo em uma única imagem e use as propriedades de CSS background-image e background-position para exibir o segmento desejado da imagem.

Image maps combinam múltiplas imagens em uma única imagem. O tamanho total será o mesmo, mas reduzirá o número de requisições HTTP, acelerando a página. Image maps somente funcionam se as imagens são contíguas na página, como uma barra de navegação. Definir as coordenadas de image maps pode ser monótona e com chance de erro. Usar image maps para navegação não é acessível também, logo não é recomendado.

Inline images usam o esquema data: URL para embutir os dados da imagem na página atual. Isto pode aumentar o tamanho de seu HTML. Combinar inline images dentro de suas folhas de estilo (armazenadas em cache) é uma maneira de reduzir as requisições HTTP e prevenir o aumento de tamanho de suas páginas. Inline images também não são suportadas na maioria dos browsers.

Reduzir o número de requisições HTTP na sua página é a primeira coisa a se fazer quando o assunto é performance. É a parte mais importante de uma otimização de desempenho quando estamos falando de visitantes de primeira viagem. Os números de diversas pesquisas indicam que 40-60% dos visitantes diários de seu site estão com o cache vazio. Tornar a sua página rápida para estes visitantes de primeira viagem é a chave para uma melhor experiência do usuário.

#2 – Utilize os cabeçalhos Expires e Cache-Control

Existem dois aspectos para esta regra:

  • Para componentes estáticos: implemente a política “Never expire” configurando o cabeçalho Expires para um futuro distante
  • Para componentes dinâmicos: use um cabeçalho Cache-Control apropriado para ajudar o browser com requisições condicionais

Projetos de Web pages estão se tornando cada vez mais ricos, o que significa mais scripts, folhas de estilo, imagens e animações na página. Um visitante de primeira viagem que chegar na sua página terá de realizar diversas requisições HTTP, porém utilizando o cabeçalho Expires você pode tornar estes componentes armazenáveis em cache. Isto previne requisições HTTP desnecessárias em visualizações de página subsequentes. Os cabeçalhos Expires são muitas vezes utilizados com imagens, mas eles devem ser usados em todos componentes incluindo scripts, folhas de estilo e componentes Flash.

Browsers (e proxies) usam cache para reduzir o número e tamanho das requisições HTTP, tornando o carregamento das páginas mais rápido. Um webserver utiliza o cabeçalho Expires na resposta HTTP para dizer ao cliente por quanto tempo um componente pode ser armazenado em cache. Este é um cabeçalho com expiração muito distante, dizendo ao browser que esta resposta não deve ser removida de cache até 15 de abril de 2012.

Se o seu servidor é Apache, use a diretiva ExpiresDefault para definir uma data de expiração relativa à data atual. Este exemplo de diretiva ExpiresDefault define a data de expiração para daqui a 10 anos, a partir do instante da requisição.

Tenha em mente, se você usar um cabeçalho para expiração futura você terá de trocar o nome do arquivo do componente toda vez que o componente for alterado. Geralmente as empresas o fazem como parte do processo de lançamento de uma nova versão do site: um número de versão do arquivo é incluído no nome do arquivo, por exemplo, luiztools_2.0.6.js.

Usar um cabeçalho de expiração futura afeta somente as visualizações de página feitas depois da primeira visita do usuário ao seu site. Não há efeito algum quando o usuário visita seu site pela primeira vez e o cache do browser está vazio. Entretanto o impacto desta melhoria de performance depende de quantas vezes os usuários acessam suas páginas com o cache cheio. Através do uso do cabeçalho Expires, você aumenta o número de componentes que são armazenados em cache pelo browser e re-utiliza em subsequentes visualizações de página sem enviar um único byte através da conexão de Internet.

#3 – Componentes GZip

O tempo de transferir uma requisição e uma resposta HTTP pela rede pode ser significativamente reduzido por decisões feitas no front-end do webmaster. É verdade que a largura de banda do usuário-final, provedor de Internet, proximidade dos roteadores, etc. estão além do controle do time de desenvolvimento. mas há outras variáveis que afetam os tempos de resposta. A compressão reduz os tempos de resposta diminuindo o tamanho da resposta HTTP.

Iniciando com HTTP/1.1, os clientes web indicam suporte à compressão com o cabeçalho Accept-Encoding na requisição HTTP.

Se o servidor web vê este cabeçalho na requisição, ele pode comprimir a resposta usando um dos métodos listados pelo cliente. O servidor web notifica o cliente web disto via cabeçalho Content-Encoding na resposta.

Gzip é o método de compressão mais popular e efetivo atualmente. Ele foi desenvolvido pelo projeto GNU e padronizado pela RFC 1952. Outro método de compressão existente é o deflate, mas ele é menos eficiente e menos popular.

Gzipar geralmente reduz o tamanho da resposta em cerca de 70%. Aproximadamente 90% do tráfico atual de Internet que viaja através dos browsers suportam gzip. Se você usa Apache, o módulo de configuração do gzip depende de sua versão: Apache 1.3 usa mod_gzip enquanto Apache 2.x usa mod_deflate.

Existem problemas conhecidos com browsers e proxies que podem causar problemas com compressão HTTP, felizmente estes problemas estão restritor à usuários de browsers antigos.

Servidores escolhem o que será comprimido, baseado no tipo de arquivo. tipicamente eles são bem limitados e a maioria dos sites somente zipa seus documentos HTML. Também é possível zipar seus scripts e stylesheets, mas muitos sites perdem essa oportunidade. Na verdade é possível comprimir qualquer texto incluindo XML e JSON. Imagens e arquivos PDF não devem ser zipados porque eles já estão em formatos comprimidos. Tentar diminuir ainda mais seu tamanho não somente irá gastar CPU à toa como pode aumentar o tamanho dos arquivos.

Gzipar todos os arquivos possíveis é uma maneira fácil de reduzir o peso das páginas e acelerar a experiência do usuário..

#4 – Folhas de Estilo no Início

Colocar as folhas de estilo no HEAD  do documento faz com que as páginas pareçam estar carregando mais rápido. Isto se deve ao fato de que colocando as folhas de estilo no topo da página permite que ela se renderize progressivamente.

Especialistas em front-end que cuidam de performance querem que uma página carregue progressivamente; isto é, nós queremos que o browser exiba algum conteúdo o mais rápido possível. isto é especialmente importante para páginas com um monte de conteúdo e para usuários com internet ruim. A importância de dar aos usuários um feedback visual, como indicadores de progresso, é largamente pesquisada e documentada. No caso de páginas web, a própria renderização da página é o melhor indicador de progresso! Quando o browser carrega a página progressivamente o usuário vai assistindo sua construção: o cabeçalho, a barra de navegação, o logo, etc. tudo serve como feedback visual para o usuário que está esperando pela página. Isto melhora bastante a experiência do usuário.

O problema em colocar as folhas de estilo próximas ao rodapé do documento é que isto proíbe a renderização progressiva em muitos browsers, incluindo o Internet Explorer. estes browsers bloqueiam a renderização para redefinir o redesenho de elementos na página, caso seus estilos sejam alterados. O usuário fica vendo uma tela em branco.

A especificação HTML claramente diz que as folhas de estilo devem ser incluídas no HEAD da página: “Diferente de A, [LINK] deve somente aparecer na seção HEAD de um documento, embora ele possa aparecer inúmeras vezes.” Obter uma tela branca ou fazer a tela piscar sem estilo não valem este risco. A melhor solução é seguir a especificação HTML e carregar suas folhas de estilo no HEAD do documento.

#5 – Scripts no Final

O problema dos scripts é que eles bloqueiam downloads paralelos. A especificação HTTP/1.1 sugere que os browsers não baixem mais de dois componentes em paralelo por hostname. Se você obtém suas imagens de múltiplos hostnames, você pode obter mais de dois downloads simultâneos. Entretanto, quando um script está sendo baixado, o browser não pode iniciar outros downloads, mesmo em diferentes servidores.

Em algumas situações não é fácil mover os scripts para o final da página. Se, por exemplo, o script usar document.write para inserir parte do conteúdo da página, ele não pode ser movido para baixo da mesma. Podem existir problemas de escopo também. Em muitos casos, existem maneiras para contornar esta situação.

Uma sugestão alternativa é utilizar scripts deferidos (deferred). O atributo DEFER indica que o script não contém document.write, e é uma dica aos browsers para que eles continuem renderizando. Infelizmente, o Firefox não suporta o atributo DEFER. No Internet Explorer, o script pode ser deferido, mas não tanto quanto desejado. Se o script puder ser deferido, ele também pode ser movido para o final da página. Isto faz com que suas páginas carreguem mais rápido..

#6 – Use CSS e JavaScript externos

Muitas dessas regras de performance lidam com como os componentes externos são gerenciados. Entretanto, antes dessas considerações surgirem você deve perguntar uma questão ainda mais básica: o Javascript e CSS devem estar em arquivos externos ou na própria página?

Usar arquivos externos no mundo real geralmente produz páginas mais rápidas porque estes arquivos são mantidos em cache pelo browser. JavaScript e CSS que estão escritos no HTML são baixados cada vez que o documento é requisitado. Isto reduz o número de requisições HTTP que são necessárias, mas aumenta o tamanho do documento HTML. Por outro lado, se o JavaScript e o CSS estão em arquivos externos em cache no browser, o tamanho do HTML é reduzido sem aumentar o número de requisições HTTP.

A chave, então, é a frequência cujos componentes externos são mantidos em cache relativo ao número de documentos HTML requisitados. Este fator, embora difícil de quantificar, pode ser analisado usando várias métricas. Se os usuários de seu site tem muitos pageviews por sessão e muitas de suas páginas reusam os mesmos scripts e estilos, há um grande potencial de ser beneficiado pelo cache de arquivos externos.

Muitos web sites caem no meio dessas métricas. Para estes sites, a melhor solução geralmente é usar JavaScript e CSS em arquivos externos. A única exceção fica para as home pages, onde escrever o código JS e CSS é preferível. Home pages que possuem poucos (quase sempre um) page view por sessão pode encontrar resultados mais rápidos escrevendo JS e CSS no próprio HTML.

#7 – Minimize JavaScript e CSS

Minificação é a prática de remover caracteres desnecessários do código para reduzir seu tamanho, melhorando tempos de carregamento. Quando o código é minificado todos os comentários são removidos, bem como espaços em branco desnecessários. No caso de JavaScript, isto melhora a performance porque seu tamanho é reduzido e consequentemente o tempo de download também. Duas ferramentas populares para minificar JavaScript são JSMin e YUI Compressor. O YUI Compressor também pode minificar CSS.

Ofuscação é uma alternativa de otimização que pode ser aplicada ao código fonte. Ela é mais complexa que minificação e algumas vezes gera bugs como resultado da ofuscação. Em uma pesquisa americana notou-se que códigos minificados obtém 21% de redução d etamanho, contra 25% da ofuscação. Embora a ofuscação mostre um desempenho melhor, minificar o JS tem menos risco.

Em adição à minificação de scripts e estilos, tags

Uma alternativa em PHP seria criar uma função chamada inserirScript.

Além de prevenir que o mesmo script seja inserido múltiplas vezes, esta função pode gerenciar outros problemas com scripts, como checagem de dependência e tudo o mais que sua imaginação permitir.

#8 - Carregue componentes após o load

Você deve olhar para sua página e se questionar: "O que é absolutamente necessário para que a página renderize?". O resto do conteúdo e dos componentes podem esperar.

JavaScript é um candidato ideal para ser carregado depois do evento load. Por exemplo se você tem um código JavaScript para fazer animações em botões, eles podem esperar, porque as animações só vão fazer sentido depois que toda página tiver sido renderizada. Outros locais a se dar uma olhada incluem conteúdo oculto (conteúdos que aparecem somente após alguma interação do usuário) e algumas imagens que não são visualizadas em um primeiro momento.

Para fazer com que imagens sejam carregadas somente quando estejam visíveis (ou próximas de estarem visíveis), sugiro o uso do plugin Lazy Load.

É bacana quando os objetivos de performance estão alinhados com as melhoras práticas web. Neste caso, a idéia de aperfeiçoamentos progressivos nos diz que Javascript, quando suportado, pode melhorar a experiência do usuário, mas você tem que ter certeza de que a página funciona mesmo sem JavaScript. Então depois que tiver certeza disso, você pode aperfeiçoar ela com alguns scripts pós-carregados que dêem mais recursos à página, como botões animados.

#9 - Carregue componentes antes do load

Pré-carregar pode ser entendido como o oposto de Pós-carregar, mas na verdade ele possui um objetivo diferente. Pré-carregar componentes lhe dá uma vantagem sobre o tempo ocioso do browser, quando ele poderia, por exemplo, estar carregando imagens que serão usadas futuramente. A idéia é quando o usuário troque de página, ele tenha uma experiência ainda mais rápida do que na primeira página.

Existem vários tipos de pré-carregamento:

  • Não-condicional: assim que o load inicia, vá em frente e carregue uns componentes extras. O site do Google utiliza esta técnica, carregando todas imagens através de CSS Sprite que serão usadas nas páginas seguintes (página de resultados, por exemplo)
  • Condicional: baseado na ação do usuário você faz um palpite do que o usuáro está pensando e já carrega os próximos componentes. O Yahoo usa esta técnica na caixa de busca deles, carregando alguns componentes conforme o que o usuário digita na caixa de busca.
  • Antecipado: esta técnica é utilizada quando você está pensando em trocar o layout do seu site em breve. Geralmente você ouve coisas do tipo "Muito legal o novo site do fulano, mas o antigo era mais rápido.". Isso acontece porque o site antigo já estava com os elementos em cache, como imagens, JS e CSS, enquanto que no novo site tudo teve de ser baixado. O pré-carregamento antecipado consiste em fazer com que seus visitantes do site antigo já baixem alguns componentes e deixem-os em cache no navegador, para quando o novo site for pro ar, a transição seja mais suave. Faça isso linkando CSS e JS antecipados, alguns dias antes do lançamento. Colocar algumas imagens sobrepostas também pode ajudar.

#10 - Divida componentes entre domínios

Dividir os componentes entre domínios pode maximizar o download paralelo de componentes. Garanta que não está usando mais do que 2-4 domínios por causa do tempo de resolução de DNS ou a técnica sairá pela culatra. Por exemplo, você pode hospedar seu HTML e conteúdo dinâmico em www.teste.com.br e dividir componentes estáticos ente img.teste.com.br e css-js.teste.com.br.

#11 - Fora 404!

Requisições HTTP são recursos "caros" à performance de um site o que torna uma imensa burrice permitir erros 404 em seu site por preguiça ou ignorância.

Alguns sites possuem páginas 404 bacanas que ajudam o usuário a encontrar o que não acharam na URL acessada, entretanto, mesmo estas páginas consomem recursos do servidor. Pior ainda é quando os erros 404 são causados por links de JS e CSS quebrados. No primeiro caso, além de gerar uma custosa requisição, o carregamento do restante da página irá travar até que o referido erro aconteça porque o download de javascript não permite outros downloads em paralelo. Como se não fosse o bastante, muitos browsers tentarão ler a página 404 procurando algum script útil pois buscavam um arquivo de scripts...

 

#12 - Otimize as imagens

Depois que o designer tenha terminado de criar as imagens para sua página, existem ainda algumas coisas que você pode fazer antes de enviar as imagens para seu servidor.

  • você pode verificar os GIFs e ver se eles estão usando um tamanho de paleta correspondente ao número de cores na imagem. Usando o software ImageMagick é fácil de verificar com o comando: identify -verbose imagem.gif. Quando você ver que uma imagem está usando 4 cores e uma palheta de 256 cores, há espaço para melhoria.
  • Tente converter GIFs para PNGs e ver se há alguma economia. Geralmente tem. Desenvolvedores muitas vezes hesitam em usar PNGs devido ao suporte limitado nos browsers, mas isto é coisa do passado. O único problema real é a transparência-alfa em PNGs true color, mas novamente, GIFs não são true color e não suportam transparência variável. Então qualquer coisa que um GIF possa fazer um PNG também faz (exceto animações). Este simples comando do ImageMagick gera PNGs seguros para usar: convert image.gif image.png
  • Execute o aplicativo pngcrush (ou qualquer outro otmizador de PNG) em todos seus PNGs, por exemplo, com este comando: pngcrush imagem.png -rem alla -reduce -brute resultado.png
  • Execute o aplicativo jpegtran em todos seus JPEGs. Esta ferramenta faz operações sem perdas em JPEGs como rotação e também pode ser usada para otimizar e remover comentários e outras informações inúteis (como informações EXIF) em suas imagens. Ex: jpegtran -copy none -optimize -perfect imagem.jpg destino.jpg
  • se estiver usando WordPress, use o excelente plugin EWWW Image Optimizer que permite usar recursos de diferentes algoritmos de compressão na sua pasta de mídias com apenas um clique
  • no desktop, use o software ImageOptim que é excelente para executar diversos algoritmos de compressão lossless e lossy em suas imagens, conforme quiser. É muito fácil de usar, basta arrastar as imagens para a janela dele e mandar converter.
ImageOptim
ImageOptim

Uma outra dica sobre otimização de imagens é não escalar imagens no HTML. Não use uma imagem grande que você precise definir a largura e altura dela em HTML. Se você precisar fazer isso:

então sua imagem (meucachorro.jpg) deve ter o tamanho de 100x100px ao invés redimensionar uma imagem de 500x500px, por exemplo.

#13 - Otimize seus CSS Sprites

Aqui vão algumas dicas que deixarão seus sprites ainda mais otimizados:

  • organizar as imagens no sprite horizontalmente ao invés de verticalmente resultará em arquivos menores
  • combinar cores similares em uma sprite lhe ajuda a manter a contagem de cores baixa, idealmente abaixo de 256 cores para que se encaixe em um arquivo PNG8.
  • não deixe grandes espaços entre as imagens do sprite. Isto não afeta muito o tamanho do arquivo mas requer menos memória do usuário para descomprimir a imagem em um mapa de pixels. Uma imagem 100x100 tem 10 mil pixels, enquanto uma 1000x1000 tem um milhão.

#14 - Use um CDN

As redes de entrega de conteúdo (CDNs) como a Cloudflare e o AWS Cloudfront são excelentes para turbinar a velocidade de entrega dos seus arquivos estáticos, especialmente se você tem muitos acessos de fora do país onde seu site está hospedado. Se você usa a hospedagem da Umbler, basta ativar o add-on gratuito da Cloudflare no seu painel de controle.

Um provedor de CDN nada mais faz do que uma cópia de todos seus arquivos estáticos em diversos servidores espalhados pelo mundo. Assim, quando um usuário requisita um desses arquivos no seu site, ele entrega a cópia que está mais perto do usuário, fornecendo uma entrega mais rápida.

#15 - Use a versão mais recente das tecnologias do site

Entre uma versão e outra de tecnologias famosas como PHP, .NET e Node.js muitas das melhorias são de desempenho, pois as aplicações e sites web estão cada vez mais exigentes. Sendo assim, usar a versão mais recente da sua plataforma de desenvolvimento não é apenas uma questão de estar na vanguarda, mas sim de tirar o máximo proveito da tecnologia que seu site usa.

Isso é especialmente verdade na plataforma WordPress por exemplo (a que eu uso para este blog). Não apenas você deve manter seu WordPress atualizado (por uma questão de segurança), mas também o seu PHP. Depois que mudei para a versão 7 do PHP meu blog ficou muito mais rápido e consumindo menos recursos do meu plano de hospedagem.

O mesmo é válido para .NET, se você puder migrar para ASP.NET Core, que está estupidamente mais leve e veloz que o framework tradicional.

E por fim, o Node.js nas novas versões (8+) tem usado recursos mais modernos do motor de JavaScript V8 do Google que o deixaram ainda mais poderoso.

Posts complementares a esse são:

E você, tem alguma dica de performance que eu não listei aqui?

Como diminuir o tamanho do apk Android: 3 dicas incríveis!

O post de hoje foi pesquisado e escrito por mim, mas com as técnicas descobertas e aplicadas pelo meu colega de serviço Cleber Henriques, líder técnico de Android no Banco Agiplan. Com essas técnicas ele conseguiu reduzir o tamanho do nosso apk em torno de 50% usando as técnicas abaixo descritas.

Tem pouco mais de um mês que estou trabalhando no Banco Agiplan. Somos um banco com um foco muito grande no mobile banking, uma vez que não possuímos agências físicas, sendo puramente um banco digital. Como um banco de varejo com um grande foco nas classes C e D, temos muitos correntistas com smartphones extremamente simples, sem grandes recursos e isso inclui armazenamento interno, que concorremos com o espaço consumido pelo Whatsapp, jogos e é claro, o Facebook, o devorador de armazenamento!

Conseguir gerar um apk pequeno, que não consuma muitos recursos do smartphone do nosso cliente, é uma garantia que teremos de que a) ele vai conseguir baixar o app rapidamente, talvez até com 3G e b) ele não vai desinstalar nosso app caso fique sem espaço no smartphone. Ele vai desinstalar apps que realmente ocupem um espaço grande.

Se o app que você trabalha também possui um público semelhante, usar as dicas abaixo podem ajudá-lo enormemente. Obviamente o ganho com estas técnicas pode variar para mais ou para menos, dependendo dos assets que você possui e que serão otimizados.

#1 – Otimize suas imagens com o ImageOptim

ImageOptim
ImageOptim

Na maior parte das vezes, o “inchaço” dos apps é devido à grande quantidade de imagens necessárias para se construir um app bonito e com boa usabilidade. Claro que isso não é uma regra, mas é o que geralmente acontece.

Assim como na web, focar seus esforços de otimização nas imagens é atacar 20% do app que geram 80% do tamanho de seu app, como no bom e velho princípio de Pareto.

Comece usando o ImageOptim, uma aplicação gratuita e de código aberto disponível para várias plataformas que diminui o tamanho de imagens JPG, PNG e GIF sem perda de qualidade usando diversas técnicas e aplicações diferentes (AdvPNG, AdvanceCOMP, OptiPNG, Pngcrush, JpegOptim, jpegtran, Gifsicle e PNGOUT).

Não tem bruxaria aqui, apenas matemática. Através do descarte de informações “inúteis” para o app como comentários e perfis de cor, consegue-se reduzir de 20-60% do tamanho original de imagens não otimizadas. Para quem já usou o recurso “Save for the Web” do Photoshop, sabe que os ganhos são bem expressivos, pois a mecânica aqui é a mesma.

O uso é muito simples: você apenas arrasta as imagens para dentro da janela do software e elas são otimizadas.

#2 – Converta suas imagens para WebP

Imagens que não possuam transparência podem ser convertidas para o formato WebP, que é um formato de imagem criado pelo Google que fornece imagens extremamente comprimidas sem perdas de qualidade aparente (assim como JPG), em um algoritmo de compressão muito superior ao JPG tradicional. Imagens WebP são suportadas a partir do Android 4.0 (API 14) sendo possível usar este formato para imagens com transparência se definir o SDK mínimo do seu projeto como 4.3 (API 18).

O Android Studio pode converter PNG, JPG, BMP ou imagens GIF estáticas para o formato WebP. Você pode converter imagens individuais ou pastas de imagens. Para converter uma imagem ou pasta de imagens, faça o seguinte:

  • Clique com o botão direito do mouse em uma imagem ou pasta de imagens, e então escolha a opção “Convert to WebP”.
  • A janela “Converting Images to WebP” vai se abrir, sendo que as configurações default dependem do SDK mínimo que estiver usando no app:
WebP Convert Image
WebP Convert Image
  • Selecione tanto lossy quanto lossless, sendo que lossless (sem perdas de qualidade) está disponível apenas com SDK mínimo 18+. Se você selecionar lossy (com perdas), defina a qualidade da codificação e se deseja ou não ver um preview da imagem convertida antes de salvar.
    Você também pode escolher pular a conversão de quaisquer imagens cuja versão final fique com tamanho maior da versão original, ou ainda imagens com transparência. Uma vez que o Android Studio somente permite criar imagens WebP transparentes se seu SDK mínimo for 18+, o checkbox “Skip images with transparency/alpha channel”é automaticamente selecionado se seu SDK mínimo for menor de 18.
  • Click OK para iniciar a conversão.  Se você está convertendo mais de uma imagem, a conversão é um passo único que não pode ser desfeito, tenha isso em mente.
    Se você selecionou a conversão lossless, a mesma acontecerá imediatamente. Suas imagens serão convertidas no lugar das originais. Se você selecionou lossy, continue lendo os passos abaixo.
  • Se você selecionou lossy, e escolheu ver o preview de cada imagem convertida antes de salvar, o Android Studio vai lhe mostrar cada imagem durante a conversão para que você inspecione o resultado. Caso contrário, ele fará a compressão de todas imagens automaticamente. Durante a etapa de preview, você pode ajustar a configuração de qualidade de cada imagem, como abaixo.
Qualidade da conversão
Qualidade da conversão

Na esquerda temos a imagem original, e na direita a comprimida, exibindo o tamanho dos arquivos (note um ganho de 68% nesse exemplo). Ajustando o slider você vê em tempo real as mudanças na imagem comprimida, sendo que na área central aparecem os pixels diferentes nelas.

#3 – Exporte seu apk por densidade de tela

Por padrão, o Android trabalha com as imagens para diferentes resoluções de tela tendo diferentes versões de cada imagem, certo? Isso é feito na pasta drawables, onde temos subpastas para densidades de tela como ldp, mdpi, hdpi, etc, conforme aumentam o tamanho e densidade das telas. Não é raro ter três versões de cada imagem o que, mesmo otimizadas, consome um grande espaço dentro do seu app.

Não seria muito legal se o seu app tivesse somente as imagens necessárias para o “tipo” de dispositivo no qual ele está sendo instalado? Assim, quem tem um Android low-end teria apenas pequenas imagens, enquanto que os usuários felizes de Androids high-end teriam apenas as imagens grandes.

Isso é possível de ser feito, usando o próprio Android Studio, mais especificamente usando o Gradle adequadamente. O Gradle é tão poderoso e versátil que não permite apenas criar apps com imagens diferentes, mas com código diferente também (usando ABI – Application Binary Interface)! Além disso, embora eu esteja mencionando aqui o recurso de exportar vários apks baseados em densidade de tela, você exportar vários apks baseado em variáveis de build também (mas isso fica para outro tutorial).

Para configurar seu build para múltiplos apks, adicione um bloco ‘splits’ no seu arquivo build.gradle a nível de módulo. Dentro do bloco ‘splits’, forneça um bloco ‘density’ que especifique como o Gradle deve gerar APKs por densidade. No seu bloco density, forneça uma lista de densidades de tela desejadas e tamanhos de tela compatíveis.

A lista de tamanhos de tela compatíveis deve somente ser usada se você precisa de elementos <compatible-screens> no manifesto de cada APK.

As opções seguintes do Gradle são usadas para configurar múltiplos APKs baseados em densidade de tela:

  • enable: se true, Gradle gera múltiplos APKs baseados nas densidades de tela que você definir. O valor default é false.
  • exclude/include: uma lista de densidades separadas por vírgula que o Gradle deve ignorar ou adicionar na geração de múltiplos APKs (serve para excluir as densidades que seu app não suporta).
  • reset(): limpa a lista default de densidades de tela. Use somente em conjunto com o elemento include. O exemplo a seguir define a lista de densidades para somente ldpi e xxhdpi chamando reset() para limpar a lista e depois usando include.

  • compatibleScreens: especifica uma lista de tamanhos de tela compatíveis separados por vírgula, injetando nós <compatible-screens> no manifesto de cada APK. Esta configuração fornece um jeito conveniente de gerenciar tanto densidade de tela quanto tamanho na mesma seção. Entretanto, tenha em mente que usar <compatible-screens> pode limitar os tipos de dispositivos nos quais o seu app irá funcionar.

Para garantir o máximo de compatibilidade, o Gradle sempre irá gerar um APK universal, mais “inchado”, que contém todos os recursos, independente de densidade, que servirá de fallback para os dispositivos que não se encaixem nos padrões estabelecidos. Esta versão universal deve ser publicada juntamente com as demais.

O exemplo seguinte gera um APK separado para cada densidade listada, exceto ldpi, xxhdpi, e xxxhdpi. Isto é feito usando exclude para remover três densidades da lista default.

Para mais detalhes de como distribuir o seu app para tipos específicos de telas e dispositivos, consulte este artigo.

Uma vez que você configure o seu build.gradle a nível de módulo para compilar múltiplos APKs, clique em Build > Build APK para compilar todos os apks para o módulo selecionado no painel Project. Para mais informações, consulte a documentação oficial.

E aí, o que achou do artigo de hoje? Conseguiu alguma economia no seu app? Coloque outras técnicas que você conhece nos comentários!

* OBS: curtiu o post? Então dá uma olhada no meu livro de Android clicando no banner abaixo pra aprender a criar outros tantos apps incríveis!

Criando apps para empresas com Android