Remover código não utilizado

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

Captura de tela do aplicativo

Medir

É sempre uma boa ideia medir o desempenho de um site antes de adicionar otimizações.

  • Para visualizar o site, pressione View App. Em seguida, pressione Fullscreen tela cheia.

Clique no gatinho favorito. 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 as Ferramentas do desenvolvedor.
  2. Clique na guia Rede.
  3. Marque a caixa de seleção Desativar cache.
  4. Atualize o app.

Tamanho original do pacote 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 samaritano ao fornecer um aviso para que os desenvolvedores não importem o pacote inteiro, mas apenas os componentes que são usados. Em outras palavras, há bibliotecas não utilizadas que podem ser removidas neste 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.

Como analisar o pacote

Há duas dependências principais no aplicativo:

  • Firebase: uma plataforma que oferece vários serviços úteis para aplicativos da Web, iOS ou Android. 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? Bem, uma das razões é que qualquer dependência pode ter suas próprias dependências. Portanto, há muito mais do que apenas duas se cada profundidade/ramo da "árvore" de dependência for considerado. É fácil para um aplicativo se tornar grande relativamente rápido se muitas dependências forem incluídas.

Analise o bundler para ter uma ideia do que está acontecendo. Há várias ferramentas diferentes criadas pela comunidade que podem ajudar a fazer isso, como 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 como um plug-in no final do arquivo na matriz plugins:

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

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

Analyzer de pacotes do Webpack

Não é tão fofo quanto ver gatinhos 🐱, mas é muito útil. 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 redução ou compactação.
Tamanho analisado Tamanho do pacote real dentro do pacote depois que ele foi compilado. A versão 4 do webpack (usada neste aplicativo) minimiza os arquivos compilados automaticamente. É por isso que o tamanho é menor do que o tamanho da estatística.
Tamanho compactado Tamanho do pacote depois de compactado com codificação gzip. Este tópico é abordado em um guia separado.

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

Como remover pacotes não usados

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

  • firestore
  • auth
  • storage
  • messaging
  • functions

Todos esses são serviços incríveis fornecidos pelo Firebase. Consulte a documentação para saber mais. No entanto, nenhum deles está sendo usado no aplicativo, então não há razão para importá-los.

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

  • Remova BundleAnalyzerPlugin da 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 ser carregado 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 Network 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 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 esforço, 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 ser feito. 😈

Como remover pacotes desnecessários

Ao contrário do Firebase, a importação de partes da biblioteca moment não pode ser feita com tanta facilidade, mas talvez ela possa ser removida por completo.

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ífica representada pelo número de milissegundos decorridos desde 1º de janeiro de 1970 00:00 UTC. Se a data e a hora atual puderem ser calculadas no mesmo formato, uma pequena função para encontrar a idade de cada gatinho em semanas provavelmente poderá ser criada.

Como sempre, tente não copiar e colar enquanto você segue as instruções. 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 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 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>
    `})
});

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

Tamanho do pacote reduzido para 225 KB

O tamanho do nosso pacote foi reduzido em mais da metade.

Conclusão

Com este codelab, você terá um bom entendimento de como analisar um pacote específico e por que pode ser 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.

Em relação à remoção de bibliotecas não utilizadas, tente descobrir quais partes de um pacote estão sendo usadas e quais não estão. Para um pacote misterioso que parece não estar sendo usado em lugar nenhum, dê um passo atrás e verifique quais dependências de nível superior podem precisar dele. Tente encontrar uma maneira de separar um do outro.

Quando se trata de remover bibliotecas desnecessárias, as coisas podem ficar 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 neste aplicativo pode parecer a coisa certa a fazer sempre, mas e se houvesse fusos horários e localidades diferentes que precisassem ser tratadas? Ou e 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 troca, e é importante avaliar se vale a pena a complexidade e o esforço de lançar uma solução personalizada em vez de depender de uma biblioteca de terceiros.