Remover código não utilizado

Neste codelab, vamos melhorar o desempenho do seguinte aplicativo removendo dependências não usadas e desnecessárias.

Captura de tela do aplicativo

Medir

É sempre bom medir o desempenho de um site antes de adicionar otimizações.

  • Para visualizar o site, pressione Ver app e depois Tela cheia tela cheia.

Clique no gatinho que você mais gosta. O Realtime Database do Firebase é usado neste aplicativo. Por isso, a pontuação é atualizada em tempo real e sincronizada com todas as outras pessoas que usam o aplicativo. 🐈

  1. Pressione "Control+Shift+J" (ou "Command+Option+J" no Mac) para abrir o DevTools.
  2. Clique na guia Rede.
  3. Marque a caixa de seleção Desativar cache.
  4. Recarregue o app.

Tamanho original do pacote de 992 KB

Quase 1 MB de JavaScript está sendo enviado para carregar esse aplicativo simples.

Confira os avisos do projeto no DevTools.

  • Clique na guia Console.
  • Verifique se Warnings está ativado no menu suspenso de níveis ao lado da entrada Filter.

Filtro de avisos

  • Confira o aviso exibido.

Aviso do console

O Firebase, uma das bibliotecas usadas neste aplicativo, está sendo um bom samaritano ao fornecer um aviso para informar aos desenvolvedores que não importem todo o pacote, mas apenas os componentes usados. Em outras palavras, há bibliotecas não utilizadas que podem ser removidas desse aplicativo para que ele carregue mais rápido.

Há também casos em que uma biblioteca específica é usada, mas pode haver uma alternativa mais simples. O conceito de remoção de bibliotecas desnecessárias será abordado mais adiante neste tutorial.

Analisar o pacote

Há duas dependências principais no aplicativo:

  • Firebase: uma plataforma que oferece vários serviços úteis para aplicativos iOS, Android ou da Web. Aqui, o Realtime Database é usado para armazenar e sincronizar as informações de cada gatinho em tempo real.
  • Moment.js: uma biblioteca de utilitários que facilita o processamento de datas em JavaScript. A data de nascimento de cada gatinho é armazenada no banco de dados do Firebase, e moment é usado para calcular a idade em semanas.

Como apenas duas dependências podem contribuir para um tamanho de pacote de quase 1 MB? Um dos motivos é que qualquer dependência pode ter as próprias dependências. Portanto, há muito mais do que apenas duas se cada profundidade/ramificação da "árvore" de dependência for considerada. É fácil um aplicativo ficar grande relativamente rápido se muitas dependências forem incluídas.

Analise o pacote para ter uma ideia melhor do que está acontecendo. Há várias ferramentas criadas pela comunidade que podem ajudar, como o webpack-bundle-analyzer.

O pacote dessa ferramenta já está incluído no app como um devDependency.

"devDependencies": {
  //...
  "webpack-bundle-analyzer": "^2.13.1"
},

Isso significa que ele pode ser usado diretamente no arquivo de configuração do webpack. Importe-o no início de webpack.config.js:

const path = require("path");

//...
const BundleAnalyzerPlugin = require("webpack-bundle-analyzer")
  .BundleAnalyzerPlugin;

Agora adicione-o como um plug-in no final do arquivo dentro da matriz plugins:

module.exports = {
  //...
  plugins: [
    //...
    new BundleAnalyzerPlugin()
  ]
};

Quando o aplicativo for recarregado, você verá uma visualização do pacote inteiro em vez do app.

Webpack Bundle Analyzer

Não é tão fofo quanto ver alguns gatinhos 🐱, mas é incrivelmente útil. Ao passar o cursor sobre qualquer um dos pacotes, o tamanho dele é representado de três maneiras diferentes:

Tamanho da estatística Tamanho antes de qualquer minificação ou compactação.
Tamanho analisado Tamanho do pacote real no pacote após a compilação. A versão 4 do webpack (usada neste aplicativo) minimiza os arquivos compilados automaticamente. Por isso, o tamanho é menor do que o tamanho da estatística.
Tamanho compactado com Gzip Tamanho do pacote após a compactação com codificação gzip. Esse assunto é abordado em um guia separado.

Com a ferramenta webpack-bundle-analyzer, é mais fácil identificar pacotes não usados ou desnecessários que representam uma grande porcentagem do pacote.

Remover pacotes não utilizados

A visualização mostra que o pacote firebase consiste em um lote muito mais do que apenas um banco de dados. Ele inclui pacotes adicionais, como:

  • firestore
  • auth
  • storage
  • messaging
  • functions

Esses são todos serviços incríveis fornecidos pelo Firebase (consulte a documentação para saber mais), mas nenhum deles está sendo usado no aplicativo. Portanto, não há motivo para importar todos eles.

Reverta as mudanças em webpack.config.js para ver o aplicativo novamente:

  • Remova BundleAnalyzerPlugin na lista de plug-ins:
plugins: [
  //...
  new BundleAnalyzerPlugin()
];
  • Agora remova a importação não utilizada da parte de cima do arquivo:
const path = require("path");

//...
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

O aplicativo deve carregar normalmente agora. Modifique src/index.js para atualizar as importações do Firebase.

import firebase from 'firebase';
import firebase from 'firebase/app';
import 'firebase/database';

Agora, quando o app é recarregado, o aviso do DevTools não aparece. Abrir o painel Rede do DevTools também mostra uma boa redução no tamanho do pacote:

Tamanho do pacote reduzido para 480 KB

Mais da metade do tamanho do pacote foi removida. O Firebase oferece muitos serviços diferentes e dá aos desenvolvedores a opção de incluir apenas aqueles que são realmente necessários. Neste aplicativo, apenas firebase/database foi usado para armazenar e sincronizar todos os dados. A importação firebase/app, que configura a superfície da API para cada um dos diferentes serviços, é sempre necessária.

Muitas outras bibliotecas conhecidas, como lodash, também permitem que os desenvolvedores importem seletivamente diferentes partes dos pacotes. Sem muito trabalho, atualizar as importações de biblioteca em um aplicativo para incluir apenas o que está sendo usado pode resultar em melhorias significativas de desempenho.

Embora o tamanho do pacote tenha sido bastante reduzido, ainda há mais trabalho a fazer. 😈

Remover pacotes desnecessários

Ao contrário do Firebase, importar partes da biblioteca moment não é tão fácil, mas talvez ela possa ser removida completamente.

O aniversário de cada gatinho fofo é armazenado no formato Unix (milissegundos) no banco de dados do Firebase.

Datas de nascimento armazenadas no formato Unix

É um carimbo de data/hora de uma data e hora específicas representado pelo número de milissegundos decorridos desde 1º de janeiro de 1970, às 00:00 UTC. Se a data e a hora atuais puderem ser calculadas no mesmo formato, uma pequena função para encontrar a idade de cada gatinho em semanas poderá ser criada.

Como sempre, tente não copiar e colar enquanto acompanha este tutorial. Comece removendo moment das importações em src/index.js.

import firebase from 'firebase/app';
import 'firebase/database';
import * as moment from 'moment';

Há um listener de eventos do Firebase que processa mudanças de valor no nosso banco de dados:

favoritesRef.on("value", (snapshot) => { ... })

Acima disso, adicione uma pequena função para calcular o número de semanas a partir de uma data específica:

const ageInWeeks = birthDate => {
  const WEEK_IN_MILLISECONDS = 1000 * 60 * 60 * 24 * 7;
  const diff = Math.abs((new Date).getTime() - birthDate);
  return Math.floor(diff / WEEK_IN_MILLISECONDS);
}

Nessa função, a diferença em milissegundos entre a data e hora atuais (new Date).getTime() e a data de nascimento (o argumento birthDate, já em milissegundos) é calculada e dividida pelo número de milissegundos em uma única semana.

Por fim, todas as instâncias de moment podem ser removidas no listener de eventos usando esta função:

favoritesRef.on("value", (snapshot) => {
  const { kitties, favorites, names, birthDates } = snapshot.val();
  favoritesScores = favorites;

  kittiesList.innerHTML = kitties.map((kittiePic, index) => {
    const birthday = moment(birthDates[index]);

    return `
      <li>
        <img src=${kittiePic} onclick="favKittie(${index})">
        <div class="extra">
          <div class="details">
            <p class="name">${names[index]}</p>
            <p class="age">${moment().diff(birthday, 'weeks')} weeks old</p>
            <p class="age">${ageInWeeks(birthDates[index])} weeks old</p>
          </div>
          <p class="score">${favorites[index]} ❤</p>
        </div>
      </li>
    `})
});

Agora, recarregue o aplicativo e confira o painel Rede mais uma vez.

Tamanho do pacote reduzido para 225 KB

O tamanho do nosso pacote foi reduzido em mais da metade novamente!

Conclusão

Com este codelab, você vai entender bem como analisar um pacote específico e por que é tão útil remover pacotes não usados ou desnecessários. Antes de começar a otimizar um aplicativo com essa técnica, é importante saber que isso pode ser muito mais complexo em aplicativos maiores.

Para remover bibliotecas não usadas, tente descobrir quais partes de um pacote estão sendo usadas e quais não estão. Para um pacote de aparência misteriosa que parece não estar sendo usado em nenhum lugar, verifique quais dependências de nível superior podem precisar dele. Tente encontrar uma maneira de desvincular um do outro.

Quando se trata de remover bibliotecas desnecessárias, as coisas podem ser um pouco mais complicadas. É importante trabalhar em conjunto com sua equipe e verificar se há potencial para simplificar partes da base de código. Remover moment nesse aplicativo pode parecer a coisa certa a fazer sempre, mas e se houvesse fusos horários e locais diferentes que precisassem ser processados? Ou e se houvesse manipulações de data mais complicadas? As coisas podem ficar muito complicadas ao manipular e analisar datas/horários, e bibliotecas como moment e date-fns simplificam isso significativamente.

Tudo é uma troca, e é importante avaliar se vale a pena a complexidade e o esforço para lançar uma solução personalizada em vez de usar uma biblioteca de terceiros.