Otimizando sua aplicação

Introdução

Após conhecer e entender como a experiência do usuário na web é classificada através das métricas e como coletar esses dados, esta seção apresenta algumas das principais técnicas que podem ser utilizadas para otimizar o desempenho das aplicações.

Imagens

Imagens podem consumir mais banda, pois são arquivos grandes, além de exigir mais tempo para serem renderizadas pelo navegador. Por isso, é importante assegurar que as imagens tenham tamanhos menores, melhorando a usabilidade da página, pois podem aparecer mais rapidamente para o usuário que as acessa. [1]

Compressão

Para garantir arquivos de imagem menores e que consumam menos banda do usuário, recomenda-se que os arquivos passem por um processo de compressão. Este processo pode ser classificado em duas formas: sem perdas (lossless) e com perdas (lossy) [1].

  • Sem perdas (lossless): a imagem é processada de forma que os pixels são compactados sem perdas [1]. Geralmente este processo é utilizado para comprimir arquivos nos formatos GIF e PNG, onde os metadados do arquivo são removidos, deixando a imagem intacta. Este processo não apresenta redução substancial no tamanho do arquivo [1].
  • Com perdas (lossy): a imagem é processada de forma que parte dos pixels são compactados com perdas [1]. Geralmente este processo é utilizado para comprimir arquivos nos formatos JPG e WebP. A imagem resultante pode apresentar aspecto granulado, pois parte dos pixels da imagem foram perdidos. Existem ferramentas de código aberto que podem ajudar o processo de compressão como Optimizt e Caesium - Image Compressor.

Formato (WebP)

WebP é um formato de imagem que suporta compressão com e sem perdas. De acordo com o Google, imagens em formato WebP sem perdas apresentam tamanho de arquivo 26% menor se comparadas ao formato PNG [4]. O Youtube apresentou 10% de melhora no desemepenho do carregamento de suas páginas quando adotou o WebP como o formato de suas thumbnails [3].

É importante ressaltar que até o momento, nem todos os navegadores modernos suportam WebP. por isso, é necessário utilizar um fallback em sua aplicação, para que todos os browsers consigam exibir as imagens [3]. Para isso, é possível utilizar a tag <picture>, que pode conter um ou mais elementos <source>, onde o navegador escolherá a melhor alternativa para ser exibida. Caso nenhum caso seja correspondido, o navegador exibirá o elemento <img> fornecido. [5]. Leia mais no exemplo disponibilizado pelo web.dev.

Neste link você pode acompanhar um exemplo real da mesma imagem: em formato jpeg, sem compressão, totalizando 3,1MB, e ao lado, a mesma imagem, em formato Webp, com 60% de compressão lossy.

JavaScript

Lazy load

O Lazy Loading, ou carregamento preguiçoso, é uma técnica que carrega recursos não essenciais de uma página apenas quando solicitados, que geralmente ocorre através de interações do usuário com a página, como scroll ou navegações entre páginas. A técnica permite otimizar o CRP (Critical Rendering Path) já que reduz o número de recursos necessários para renderizar a página [6].

Ainda sobre lazy loading, é possível otimizar uma aplicação utilizando de dois princípios: reduzir tamanho dos arquivos que enviamos ao navegador, pois quanto menores, mais rapidamente serão baixados no processo de renderização. O segundo princípio consiste na redução de código JavaScript enviado. Assim é possível assegurar que o navegador utilizará o código recebido por completo, baixando pequenos pacotes por vez, sem desperdiçar seus recursos compilando e executando código que não será utilizado durante o processo de navegação do usuário [7].

Code splitting

Para evitar a necessidade de baixar todos os arquivos que compõem a aplicação, os scripts podem ser divididos em diversos arquivos menores. Assim o usuário faz download apenas do código que representa uma página específica [8].

Para que uma aplicação possa ser entregue em pedaços menores ao cliente, é possível utilizar a técnica de importação dinâmica do Webpack. A técnica consiste na utilização do import como uma função, que recebe como argumento o caminho para o módulo que será dividido em um bundle menor, e retorna uma promise.

import('@/modules/components/MyComponent').then( ... )

Também é possível aplicar code splitting ao nível de rotas da aplicação, onde cada rota gera um bundle diferente. Isso significa que o usuário recebe apenas os recursos necessários para renderização da página que ele solicitou durante a navegação, sempre recebendo os recursos sob demanda. Por exemplo, caso a página sobre não seja indispensável para a navegação do usuário, pode ter a configuração “preguiçosa”:

const Page = () => import ('@/modules/pages/sobre.vue')

Entretanto, é importante ressaltar que nem todas as partes da aplicação podem ser configuradas com lazy load, como por exemplo a página inicial. Caso contrário, o usuário pode se deparar com uma página lenta.

Tree-shaking

De acordo com Jeremy Wagner, tree-shaking é uma forma de eliminar código não utilizado [9]. Atualmente, para se construir aplicações em javascript é possível utilizar diversas dependências, que são importadas diretamente nos arquivos utilizados, como, por exemplo, a biblioteca lodash:

import _ from 'lodash';

Dessa forma, a biblioteca inteira, com todas as suas funções, serão disponibilizadas para uso. Entretanto, nem sempre precisamos de todas as funções que a biblioteca disponibiliza, e por isso, a aplicação agora entrega muito código que não será utilizado, ainda assim sendo baixado pelo usuário ao acessá-la. Para contornar esse problema, o javascript permite importar apenas as funções que serão utilizadas, por exemplo:

import sortBy from 'lodash/sortBy';

Referências