Tree Shaking é um mecanismo para remover código não utilizado (dead code elimination) na etapa de compilação da aplicação. O Tree Shaking analisa a estrutura estática dos módulos ES e exclui funções, classes e variáveis exportadas que não são importadas em nenhum lugar. De acordo com a Documentação do Webpack, a configuração correta do Tree Shaking pode reduzir o tamanho do bundle em 30–60% sem alterar a funcionalidade da aplicação.
Pontos principais
Tree Shaking é uma técnica de otimização de código que exclui módulos e funções não utilizados do bundle final. O termo foi introduzido pela equipe do Rollup em 2015 e descreve metaforicamente o processo: a árvore de dependências é sacudida e os galhos não utilizados caem. Ao contrário da otimização manual, o Tree Shaking é executado automaticamente na etapa de compilação.
O Tree Shaking funciona apenas com módulos ES (ECMAScript Modules), onde as dependências são determinadas estaticamente através de import e export. O CommonJS (require/module.exports) não suporta Tree Shaking porque o require é executado dinamicamente — o bundler não pode determinar antecipadamente quais funções são realmente usadas. Bibliotecas modernas (Lodash, Moment.js, RxJS) lançam versões ES para suportar Tree Shaking.
Rollup foi o primeiro bundler a implementar Tree Shaking em 2015. Ao contrário do Webpack, o Rollup foi projetado desde o início para módulos ES e realiza uma remoção de código morto mais agressiva. O Rollup analisa não apenas exportações individuais, mas módulos inteiros: se um módulo não tem efeitos colaterais e nenhuma exportação é usada, o Rollup exclui o módulo inteiro do bundle.
O Rollup é especialmente eficaz para bibliotecas e SDKs onde cada kilobyte importa. O framework Vue.js usa o Rollup para compilar sua versão de produção. O React migrou para o Rollup em 2020. Para aplicações, o Webpack é mais comumente usado devido ao seu ecossistema mais rico de plugins (Hot Module Replacement, code splitting, CSS modules), mas para o máximo Tree Shaking ao compilar bibliotecas, o Rollup continua sendo o padrão da indústria.
A economia do Tree Shaking depende fortemente da arquitetura do projeto. Em uma aplicação React com a biblioteca Ant Design, o Tree Shaking pode remover até 70% do código dos componentes de UI. Em um projeto onde todas as importações são específicas e direcionadas, a economia será de 5–15%. De acordo com pesquisas do Webpack, a economia média é de 30–40% do tamanho do bundle.
| Tipo de código morto | Exemplo | Detecção de Tree Shaking |
|---|---|---|
| Exportação não utilizada | export function unusedHelper() | Sim |
| Importação não utilizada | import { unused } from "lib" | Sim |
| Ramo de condição morta | if (false) { ... } | Não (removido pelo minificador) |
| Função não chamada após DCE | function a(){} a() onde a não é chamada | Parcialmente |
O mecanismo do Tree Shaking é baseado em um grafo de dependências que o bundler constrói a partir de todas as declarações import/export no projeto. No primeiro estágio, o bundler percorre todos os arquivos a partir do ponto de entrada e coleta a árvore de módulos. No segundo estágio, ele analisa quais exportações de cada módulo são realmente importadas em outros módulos.
Para cada módulo, o Webpack ou Rollup marca as exportações como usadas ou não usadas. Exportações não usadas são excluídas do bundle. No entanto, o módulo em si permanece no bundle se pelo menos uma de suas exportações for usada. Um módulo pode ser completamente excluído apenas através da flag sideEffects ou se o módulo não contiver efeitos colaterais.
// utils.js — módulo com funções
export function formatDate(date) {
return date.toISOString().slice(0, 10);
}
export function formatCurrency(amount) {
return "$" + amount.toFixed(2);
}
export function slugify(text) {
return text.toLowerCase().replace(/\s+/g, "-");
}// app.js — ponto de entrada
import { formatDate } from "./utils";
const today = formatDate(new Date());
console.log(today);// Após Tree Shaking — apenas formatDate no bundle
function formatDate(date) {
return date.toISOString().slice(0, 10);
}
const today = formatDate(new Date());
console.log(today);Tree Shaking excluiu formatCurrency e slugify do bundle final porque eles não são importados no app.js. O tamanho do módulo utils.js diminuiu de 3 funções para 1. Se utils.js contiver efeitos colaterais (por exemplo, inicialização global), o Tree Shaking não pode remover nem mesmo as exportações não utilizadas.
Webpack inclui suporte integrado a Tree Shaking através do TerserPlugin no modo produção. Para habilitar o Tree Shaking, duas condições são suficientes: mode está definido como production (mode: "production") e os módulos usam sintaxe ES (import/export). O Webpack marca automaticamente as exportações não utilizadas e as passa para o Terser para remoção.
A configuração adicional de usedExports: true em optimization.webpack.config.js permite a análise detalhada do uso de exportações dentro de um módulo. Esta opção determina quais exportações são realmente usadas e quais são apenas exportadas (provided). A combinação de usedExports e Terser oferece a máxima eficiência de remoção de código morto.
// webpack.config.js — configuração de Tree Shaking
module.exports = {
mode: "production",
entry: "./src/app.js",
output: {
filename: "bundle.js",
},
optimization: {
usedExports: true,
minimize: true,
concatenateModules: true,
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules\/(?!(my-lib)\/).*/,
use: {
loader: "babel-loader",
options: {
presets: [
["@babel/preset-env", { modules: false }],
],
},
},
},
],
},
};O parâmetro chave é modules: false no @babel/preset-env. Por padrão, o Babel transforma módulos ES em CommonJS, o que mata o Tree Shaking. modules: false impede que o Babel transforme import/export, preservando a sintaxe ES para o Webpack. concatenateModules adicionalmente mescla módulos em um escopo compartilhado, reduzindo o número de IIFEs e diminuindo o tamanho do bundle.
Side effects (efeitos colaterais) são ações que um módulo realiza ao ser importado e que não estão relacionadas aos valores exportados: estilos globais (import "./styles.css"), polyfills (import "core-js/stable"), inicialização de variáveis globais ou registro de Service Worker. Se um módulo contém efeitos colaterais, o bundler não pode removê-lo do bundle com segurança, mesmo que nenhuma de suas exportações seja usada.
A flag sideEffects no package.json informa ao bundler quais módulos no pacote não têm efeitos colaterais. Para um pacote onde todos os módulos são puros (apenas exportações de funções), você deve especificar "sideEffects": false. Para pacotes com CSS ou polyfills — um array de caminhos para arquivos com efeitos colaterais: "sideEffects": ["*.css"]. Sem esta flag, o Tree Shaking não removerá nem mesmo funções não utilizadas.
Para verificar se um módulo tem efeitos colaterais, faça esta pergunta: esta importação realizará alguma ação não relacionada à exportação de valores? import "./styles.css" adiciona CSS ao DOM — isso é um efeito colateral. import { throttle } from "lodash-es" não tem efeitos colaterais — apenas torna a função throttle disponível. Os polyfills (import "core-js/stable") têm efeitos colaterais — eles modificam protótipos globais.
Para seus próprios módulos, é recomendado: extrair estilos e polyfills em pontos de entrada separados, separar utilitários puros (funções sem efeitos colaterais) de módulos com efeitos colaterais (inicialização, registro, registro de Service Worker). No package.json do projeto de nível superior, especifique "sideEffects": false apenas se todos os módulos forem puros. Se houver estilos, especifique "sideEffects": ["*.css"] precisamente.
{
"name": "my-ui-lib",
"version": "2.1.0",
"sideEffects": [
"*.css",
"polyfills.js"
],
"module": "dist/index.esm.js",
"main": "dist/index.cjs.js"
}"sideEffects": ["*.css", "polyfills.js"] significa: todos os arquivos CSS têm efeitos colaterais (não podem ser removidos) e polyfills.js também. Todos os outros arquivos JS no pacote são puros — eles podem ser shaken com segurança. O campo module especifica o caminho para a versão ES do pacote que o bundler deve usar em vez da versão CommonJS (main) para Tree Shaking.
React Native com Metro Bundler suporta uma versão limitada de Tree Shaking. O Metro não realiza análise estática completa das exportações usadas (usedExports) como o Webpack. Em vez disso, o Metro depende do Terser para remover partes não utilizadas dos módulos durante a minificação. A eficácia desta abordagem é menor do que o Tree Shaking completo no Webpack.
Para a máxima otimização de projetos React Native, é recomendado: usar bibliotecas com módulos ES (campo module no package.json), adicionar babel-plugin-transform-remove-console para remover código de depuração e configurar o Metro transformer.minifierConfig para o Terser. Além disso, o Ram Bundle (divisão do bundle em módulos) reduz o carregamento de telas não utilizadas.
Perguntas frequentes
CommonJS (require/module.exports) não suporta análise estática — o require pode ser chamado dinamicamente dentro de condições e funções. O bundler não pode determinar quais partes do módulo são realmente usadas. Apenas módulos ES com import/export estático permitem Tree Shaking.
TypeScript é totalmente compatível com Tree Shaking desde que o tsconfig.json esteja configurado para módulos ES: "module": "esnext". O compilador TypeScript deve preservar import/export sem convertê-los para CommonJS. O Babel com @babel/preset-typescript e modules: false também passa corretamente os módulos ES para o Webpack.
Webpack Bundle Analyzer é um plugin que visualiza a composição do bundle como um diagrama interativo. Se uma biblioteca estiver presente no bundle mas suas funções não forem usadas, o Tree Shaking não funcionou. Você também pode analisar o arquivo de saída: procure exportações não utilizadas no texto do bundle através de grep.
Lodash v4 é distribuído como um pacote CommonJS. Para Tree Shaking, você precisa usar lodash-es — a versão ES da biblioteca. Substitua import throttle from "lodash/throttle" por import { throttle } from "lodash-es" e configure resolve.alias no Webpack para substituir lodash por lodash-es.
Tree Shaking aumenta ligeiramente o tempo de compilação (em 5–15%) porque adiciona uma etapa de análise do grafo de dependências e marcação de exportações usadas. No modo development, o Tree Shaking geralmente está desativado por velocidade. Na produção, o tempo adicional é justificado por uma redução significativa no tamanho do bundle.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também