Otimização e eliminação de código com webpack e Babeljs
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.