Reduzir payloads de JavaScript com a divisão de código

A maioria das páginas da Web e dos aplicativos é composta por muitas partes diferentes. Em vez de enviar todo o JavaScript que compõe o aplicativo assim que a primeira página é carregada, dividir o JavaScript em vários blocos melhora a performance da página.

Este codelab mostra como usar a divisão de código para melhorar o desempenho de um aplicativo simples que classifica três números.

Uma janela do navegador mostra um aplicativo intitulado Magic Sorter com três campos para inserir números e um botão de classificação.

Medir

Como sempre, é importante medir primeiro o desempenho de um site antes de tentar adicionar otimizações.

  1. Para visualizar o site, pressione View App. Em seguida, pressione Fullscreen tela cheia.
  2. Pressione "Control+Shift+J" (ou "Command+Option+J" no Mac) para abrir as Ferramentas do desenvolvedor.
  3. Clique na guia Rede.
  4. Marque a caixa de seleção Desativar cache.
  5. Atualize o app.

Painel de rede mostrando o pacote JavaScript de 71,2 KB.

71,2 KB de JavaScript apenas para classificar alguns números em um aplicativo simples. What gives?

No código-fonte (src/index.js), a biblioteca lodash é importada e usada neste aplicativo. O Lodash fornece muitas funções de utilitário úteis, mas apenas um único método do pacote está sendo usado aqui. Instalar e importar dependências de terceiros inteiras em que apenas uma pequena parte delas está sendo utilizada é um erro comum.

Otimizar

Há algumas maneiras de reduzir o tamanho do pacote:

  1. Programar um método de classificação personalizado em vez de importar uma biblioteca de terceiros
  2. Use o método Array.prototype.sort() integrado para classificar numericamente
  3. Importe apenas o método sortBy de lodash, e não a biblioteca inteira
  4. Fazer o download do código de classificação somente quando o usuário clicar no botão

As opções 1 e 2 são métodos perfeitamente adequados para reduzir o tamanho do pacote (e provavelmente fariam mais sentido para um aplicativo real). No entanto, eles não são usados neste tutorial para fins de ensino 😈.

As opções 3 e 4 ajudam a melhorar o desempenho do aplicativo. As próximas seções deste codelab abordam essas etapas. Como em qualquer tutorial de programação, sempre tente escrever o código em vez de copiar e colar.

Importe apenas o que você precisa

Alguns arquivos precisam ser modificados para importar apenas o método único de lodash. Para começar, substitua esta dependência em package.json:

"lodash": "^4.7.0",

com este:

"lodash.sortby": "^4.7.0",

Agora, em src/index.js, importe este módulo específico:

import "./style.css";
import _ from "lodash";
import sortBy from "lodash.sortby";

E atualize como os valores são classificados:

form.addEventListener("submit", e => {
  e.preventDefault();
  const values = [input1.valueAsNumber, input2.valueAsNumber, input3.valueAsNumber];
  const sortedValues = _.sortBy(values);
  const sortedValues = sortBy(values);

  results.innerHTML = `
    <h2>
      ${sortedValues}
    </h2>
  `
});

Recarregue o aplicativo, abra o DevTools e confira o painel Network novamente.

Painel de rede mostrando o pacote JavaScript de 15,2 KB.

Para esse aplicativo, o tamanho do pacote foi reduzido em mais de quatro vezes com muito pouco trabalho, mas ainda há espaço para melhorias.

Divisão de código

O webpack é um dos agrupadores de módulos de código aberto mais usados atualmente. Em resumo, ele agrupa todos os módulos JavaScript (e outros recursos) que compõem um aplicativo da Web em arquivos estáticos que podem ser lidos pelo navegador.

O único pacote usado neste aplicativo pode ser dividido em dois fragmentos separados:

  • Um responsável pelo código que compõe nossa rota inicial
  • Um bloco secundário que contém nosso código de classificação

Com o uso de importações dinâmicas, um fragmento secundário pode ser carregado de forma lenta ou carregado sob demanda. Nesse aplicativo, o código que compõe o bloco só pode ser carregado quando o usuário pressiona o botão.

Comece removendo a importação de nível superior do método de classificação em src/index.js:

import sortBy from "lodash.sortby";

E importe-o no listener de eventos que é acionado quando o botão é pressionado:

form.addEventListener("submit", e => {
  e.preventDefault();
  import('lodash.sortby')
    .then(module => module.default)
    .then(sortInput())
    .catch(err => { alert(err) });
});

O recurso import() faz parte de uma proposta (atualmente na fase 3 do processo TC39) para incluir a capacidade de importar dinamicamente um módulo. O webpack já inclui suporte a isso e segue a mesma sintaxe estabelecida pela proposta.

import() retorna uma promessa e, quando ela é resolvida, o módulo selecionado é fornecido e dividido em um bloco separado. Depois que o módulo é retornado, module.default é usado para referenciar a exportação padrão fornecida pelo lodash. A promessa é encadeada com outra .then que chama um método sortInput para classificar os três valores de entrada. No final da cadeia de promessas, .catch() é usado para processar casos em que a promessa é rejeitada devido a um erro.

A última coisa que precisa ser feita é gravar o método sortInput no final do arquivo. Ela precisa ser uma função que retorna uma função que recebe o método importado de lodash.sortBy. A função aninhada pode classificar os três valores de entrada e atualizar o DOM.

const sortInput = () => {
  return (sortBy) => {
    const values = [
      input1.valueAsNumber,
      input2.valueAsNumber,
      input3.valueAsNumber
    ];
    const sortedValues = sortBy(values);

    results.innerHTML = `
      <h2>
        ${sortedValues}
      </h2>
    `
  };
}

Monitoramento

Atualize o aplicativo mais uma vez e observe o painel Network novamente. Apenas um pequeno pacote inicial é transferido por download assim que o app é carregado.

Painel de rede mostrando o pacote JavaScript de 2,7 KB.

Depois que o botão é pressionado para classificar os números de entrada, o bloco que contém o código de classificação é buscado e executado.

Painel de rede mostrando um pacote JavaScript de 2,7 KB seguido por um pacote JavaScript de 13,9 KB.

Observe como os números ainda são classificados.

Conclusão

A divisão de código e o carregamento lento podem ser técnicas extremamente úteis para reduzir o tamanho inicial do pacote do seu aplicativo, o que pode resultar em tempos de carregamento de página muito mais rápidos. No entanto, há algumas questões importantes que precisam ser consideradas antes de incluir essa otimização no seu app.

Interface de carregamento lento

Ao carregar de forma lenta módulos específicos de código, é importante considerar como a experiência seria para usuários com conexões de rede mais fracas. Se você dividir e carregar um bloco muito grande de código quando um usuário enviar uma ação, pode parecer que o aplicativo parou de funcionar. Portanto, mostre um indicador de carregamento.

Carregamento lento de módulos de nó de terceiros

Essa não é sempre a melhor abordagem para carregar de maneira lenta as dependências de terceiros no aplicativo, e isso depende de onde elas são usadas. Normalmente, as dependências de terceiros são divididas em um pacote vendor separado que pode ser armazenado em cache, já que elas não são atualizadas com frequência. Leia mais sobre como o SplitChunksPlugin pode ajudar você a fazer isso.

Carregamento lento com um framework JavaScript

Muitos frameworks e bibliotecas conhecidos que usam o Webpack oferecem abstrações para facilitar o carregamento lento em vez de usar importações dinâmicas no meio do aplicativo.

Embora seja útil entender como as importações dinâmicas funcionam, sempre use o método recomendado pelo framework/biblioteca para carregar módulos específicos de maneira lenta.

Pré-carregamento e pré-busca

Sempre que possível, aproveite as dicas do navegador, como <link rel="preload"> ou <link rel="prefetch">, para tentar carregar módulos essenciais ainda mais cedo. O webpack oferece suporte a ambas as dicas usando comentários mágicos em instruções de importação. Isso é explicado em mais detalhes no guia Carregar blocos críticos com antecedência.

Carregamento lento de mais do que código

As imagens podem ser uma parte significativa de um aplicativo. O carregamento lento de imagens abaixo da dobra ou fora da janela de visualização do dispositivo pode acelerar um site. Leia mais sobre isso no guia Lazysizes.