Remover código não utilizado

Neste codelab, melhore o desempenho do aplicativo a seguir removendo todas as dependências não usadas e desnecessárias.

Captura de tela do aplicativo

Medida

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

  • Para visualizar o site, pressione Ver app. Em seguida, pressione Tela cheia modo tela cheia.

Vá em frente e clique no seu gatinho favorito! O Realtime Database do Firebase é usado neste aplicativo. É por isso que 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. Atualize o app.

Tamanho do pacote original de 992 KB

Quase 1 MB de JavaScript está sendo enviado para carregar este 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, que é uma das bibliotecas usadas neste aplicativo, está sendo um bom samaritan ao fornecer um aviso para que os desenvolvedores saibam que não devem importar todo o pacote, mas apenas os componentes usados. Em outras palavras, há bibliotecas não utilizadas que podem ser removidas nesse aplicativo para que ele seja carregado mais rápido.

Também há casos em que uma biblioteca específica é usada, mas pode haver uma alternativa mais simples. O conceito de remoção de bibliotecas desnecessárias é explorado posteriormente neste tutorial.

Como analisar o pacote

Há duas dependências principais no aplicativo:

  • Firebase: plataforma que oferece vários serviços úteis para apps 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 no 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 contribuem para um tamanho de pacote de quase 1 MB? Bem, um dos motivos é que qualquer dependência pode, por sua vez, ter as próprias dependências, de modo que há muito mais do que apenas duas se cada profundidade/ramificação da "árvore" de dependência for considerada. É fácil um aplicativo se tornar grande relativamente rápido se muitas dependências forem incluídas.

Analise o bundler para ter uma ideia melhor do que está acontecendo. Há várias ferramentas criadas pela comunidade que podem ajudar com isso, 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"
},

Ou seja, 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, na matriz plugins:

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

Quando o aplicativo for recarregado, você terá uma visualização de todo o pacote, em vez do app em si.

Analisador de pacotes do Webpack

Não é tão fofo quanto ver alguns gatinhos Deliver, mas incrivelmente útil mesmo assim. Passar o cursor sobre qualquer um dos pacotes mostra 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 dentro do pacote após a compilação. A versão 4 do webpack (que é usada neste aplicativo) minimiza os arquivos compilados automaticamente, e é por isso que ele é menor que o tamanho da estatística.
Tamanho Gzip Tamanho do pacote após a compactação com a codificação gzip. Esse tópico é abordado em um guia separado.

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

Como remover pacotes não utilizados

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

  • firestore
  • auth
  • storage
  • messaging
  • functions

Todos esses serviços são incríveis fornecidos pelo Firebase. Consulte a documentação para saber mais, mas nenhum deles está sendo usado no aplicativo. Por isso, não há motivo para importar todos.

Reverta as mudanças em webpack.config.js para conferir o aplicativo de novo:

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

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

Agora, o aplicativo deve ser carregado normalmente. 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 for recarregado, o aviso do DevTools não será exibido. 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 removido. O Firebase oferece muitos serviços diferentes e dá aos desenvolvedores a opção de incluir apenas os 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 plataforma 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 fazer muito trabalho, atualizar as importações de biblioteca em um aplicativo para incluir apenas o que está sendo usado pode resultar em melhorias significativas no desempenho.

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

Como remover pacotes desnecessários

Ao contrário do Firebase, a importação de partes da biblioteca moment não é tão fácil, mas talvez seja totalmente removida.

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 representadas pelo número de milissegundos decorridos desde 1o de janeiro de 1970 à 00h00 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 provavelmente poderá ser construída.

Como sempre, tente não copiar e colar ao acompanhar as informações aqui. Comece removendo moment das importações em src/index.js.

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

No nosso banco de dados, há um listener de eventos que processa alterações de valor:

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

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

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 a 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>
    `})
});

Atualize o aplicativo e confira o painel Network mais uma vez.

Tamanho do pacote reduzido para 225 KB

O tamanho do nosso pacote foi reduzido pela metade outra vez!

Conclusão

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

Em relação à remoção de bibliotecas não usadas, tente descobrir quais partes de um pacote estão sendo usadas e quais não estão. Para conferir um pacote misterioso que parece não estar sendo usado em nenhum lugar, confira quais dependências de nível superior podem precisar dele. Tente encontrar uma maneira de os separar.

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

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