Tiago Porto

Otimização e eliminação de código com webpack e Babeljs


Tiago Porto

Tree Shaking

Com a velocidade a qual o JavaScript avança, tendo versões anuais adicionando funcionalidades, acabamos por adicionar um compilador (Babeljs um dos mais populares atualmente) em nossa stack para usufruirmos de todas essas novidades e manter compatibilidade com navegadores antigos.

Na stack também é comum utilizarmos um bundler que se encarrega de gerar nosso código final aplicando diversas otimizações. O webpack é um dos mais famosos atualmente, oferecendo várias opções de otimização, mas vamos focar em uma delas, o tree shaking.

O tree shaking é responsável pela eliminação de código não utilizado no nosso bundle o famoso dead code. Porém, com a utilização do babel-loader, precisamos configurá-lo corretamente para não perder essa funcionalidade.

Primeiramente vamos entender qual é o problema.

Um simples exemplo com módulos JavaScript:

// module.js
export const say = () => console.log('Say something')

export const scream = () => console.log('SCREAM SOMETHING')
// App entrypoint
// index.js
import { scream } from './module.js'

scream()

No index.js importamos somente a função scream do arquivo module.js. Como a aplicação não está usando a função say, não faz sentido adicioná-la ao bundle, e é isso o que faz o tree shaking, remover código não utilizado. Porém, para usufruir de novas funcionalidades (como no exemplo, ESM modules e arrow functions), para manter a compatibilidade com navegadores o Babeljs nos ajuda compilando esse código para uma versão anterior do JavaScript.

Geralmente a configuração básica do Babeljs é essa:

{
  "presets": ["env"]
}

A única mudança que precisamos fazer é setar o modules para false.

{
  "presets": [
    [
      "env",
      {
        "modules": false
      }
    ]
  ]
}

O que acontece é que o Babeljs compila o código antes do webpack, e converte os módulos para CommonJS por padrão, quando chega no webpack ele não consegue usufruir dos módulos nativos do JavaScript.

Esse é um exemplo simples, mas quando sua aplicação cresce e utiliza muitas bibliotecas, não faz sentido onerar muito o carregamento com código não utilizado.

Loose Mode

Uma outra dica: muitos dos presets do Babeljs possuem um parâmetro chamado loose, e o babel-preset-env é um deles. Basicamente o que acontece na compilação do Babeljs é, quando uma funcionalidade não é suportada na versão ES5, ele compila essa parte do código para ES5 mantendo a funcionalidade e semântica do código fonte. O que o loose mode faz é compilar para ES5, porém não se importando tanto para a semântica, e muitas vezes ele gera um código muito menor. Como o nosso bundle vai ser interpretado diretamente no navegador ou no nodejs, não precisamos nos importar tanto com a semântica aqui, já que continuamos dando manutenção no arquivo fonte.

Para habilitar:

// babel.config.js
{
  "presets": [
    [
      "env",
      {
        "modules": false,
        "loose": true
      }
    ]
  ]
}

Para alguns exemplos de código gerado pelo loose mode leia Babel: Loose mode.

PS. Sugiro cuidado com loose mode, não chega a ser recomendado leia com melhor detalhes prós e cons.

Comentários e reações