Tree Shaking es un mecanismo para eliminar código no utilizado (dead code elimination) en la etapa de compilación de la aplicación. Tree Shaking analiza la estructura estática de los módulos ES y excluye funciones, clases y variables exportadas que no se importan en ningún lugar. Según la Documentación de Webpack, una configuración correcta de Tree Shaking puede reducir el tamaño del bundle entre un 30 y un 60% sin cambiar la funcionalidad de la aplicación.
Puntos clave
Tree Shaking es una técnica de optimización de código que excluye módulos y funciones no utilizados del bundle final. El término fue introducido por el equipo de Rollup en 2015 y describe metafóricamente el proceso: se sacude el árbol de dependencias y las ramas no utilizadas caen. A diferencia de la optimización manual, Tree Shaking se ejecuta automáticamente en la etapa de compilación.
Tree Shaking solo funciona con módulos ES (ECMAScript Modules), donde las dependencias se determinan estáticamente mediante import y export. CommonJS (require/module.exports) no es compatible con Tree Shaking porque require se ejecuta dinámicamente — el bundler no puede determinar de antemano qué funciones se utilizan realmente. Las bibliotecas modernas (Lodash, Moment.js, RxJS) lanzan versiones ES para permitir Tree Shaking.
Rollup fue el primer bundler en implementar Tree Shaking en 2015. A diferencia de Webpack, Rollup fue diseñado desde el principio para módulos ES y realiza una eliminación de código muerto más agresiva. Rollup analiza no solo exportaciones individuales sino módulos enteros: si un módulo no tiene efectos secundarios y no se utiliza ninguna exportación, Rollup excluye todo el módulo del bundle.
Rollup es especialmente efectivo para bibliotecas y SDK donde cada kilobyte importa. El framework Vue.js usa Rollup para compilar su versión de producción. React migró a Rollup en 2020. Para aplicaciones, Webpack se usa más comúnmente debido a su ecosistema más rico de plugins (Hot Module Replacement, code splitting, CSS modules), pero para un Tree Shaking máximo al compilar bibliotecas, Rollup sigue siendo el estándar de la industria.
El ahorro de Tree Shaking depende en gran medida de la arquitectura del proyecto. En una aplicación React con la biblioteca Ant Design, Tree Shaking puede eliminar hasta el 70% del código de componentes UI. En un proyecto donde todas las importaciones son específicas y dirigidas, el ahorro será del 5 al 15%. Según las investigaciones de Webpack, el ahorro medio es del 30 al 40% del tamaño del bundle.
| Tipo de código muerto | Ejemplo | Detección de Tree Shaking |
|---|---|---|
| Exportación no utilizada | export function unusedHelper() | Sí |
| Importación no utilizada | import { unused } from "lib" | Sí |
| Rama de condición muerta | if (false) { ... } | No (eliminada por el minificador) |
| Función no llamada después de DCE | function a(){} a() donde a no es llamada | Parcialmente |
El mecanismo de Tree Shaking se basa en un grafo de dependencias que el bundler construye a partir de todas las declaraciones import/export del proyecto. En la primera etapa, el bundler recorre todos los archivos desde el punto de entrada y recopila el árbol de módulos. En la segunda etapa, analiza qué exportaciones de cada módulo se importan realmente en otros módulos.
Para cada módulo, Webpack o Rollup marca las exportaciones como usadas o no usadas. Las exportaciones no usadas se excluyen del bundle. Sin embargo, el módulo en sí permanece en el bundle si al menos una de sus exportaciones se utiliza. Un módulo solo se puede excluir completamente mediante la bandera sideEffects o si el módulo no contiene ningún efecto secundario.
// utils.js — módulo con funciones
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 — punto de entrada
import { formatDate } from "./utils";
const today = formatDate(new Date());
console.log(today);// Después de Tree Shaking — solo formatDate en el bundle
function formatDate(date) {
return date.toISOString().slice(0, 10);
}
const today = formatDate(new Date());
console.log(today);Tree Shaking excluyó formatCurrency y slugify del bundle final porque no se importan en app.js. El tamaño del módulo utils.js se redujo de 3 funciones a 1. Si utils.js contiene efectos secundarios (por ejemplo, inicialización global), Tree Shaking no puede eliminar ni siquiera las exportaciones no utilizadas.
Webpack incluye soporte integrado de Tree Shaking a través de TerserPlugin en modo producción. Para habilitar Tree Shaking, dos condiciones son suficientes: mode está configurado en production (mode: "production") y los módulos usan sintaxis ES (import/export). Webpack marca automáticamente las exportaciones no utilizadas y las pasa a Terser para su eliminación.
La configuración adicional de usedExports: true en optimization.webpack.config.js habilita el análisis detallado del uso de exportaciones dentro de un módulo. Esta opción determina qué exportaciones se utilizan realmente y cuáles solo se exportan (provided). La combinación de usedExports y Terser proporciona la máxima eficiencia de eliminación de código muerto.
// webpack.config.js — configuración 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 }],
],
},
},
},
],
},
};El parámetro clave es modules: false en @babel/preset-env. Por defecto, Babel transforma los módulos ES a CommonJS, lo que mata Tree Shaking. modules: false evita que Babel transforme import/export, preservando la sintaxis ES para Webpack. concatenateModules además fusiona módulos en un ámbito compartido, reduciendo el número de IIFE y disminuyendo el tamaño del bundle.
Los side effects (efectos secundarios) son acciones que un módulo realiza al ser importado y que no están relacionadas con los valores exportados: estilos globales (import "./styles.css"), polyfills (import "core-js/stable"), inicialización de variables globales o registro de Service Worker. Si un módulo contiene efectos secundarios, el bundler no puede eliminarlo del bundle de forma segura, incluso si no se utiliza ninguna de sus exportaciones.
La bandera sideEffects en package.json le indica al bundler qué módulos del paquete no tienen efectos secundarios. Para un paquete donde todos los módulos son puros (solo exportaciones de funciones), se debe especificar "sideEffects": false. Para paquetes con CSS o polyfills, una matriz de rutas a archivos con efectos secundarios: "sideEffects": ["*.css"]. Sin esta bandera, Tree Shaking no eliminará ni siquiera las funciones no utilizadas.
Para verificar si un módulo tiene efectos secundarios, hazte esta pregunta: ¿esta importación realizará alguna acción no relacionada con la exportación de valores? import "./styles.css" agrega CSS al DOM — eso es un efecto secundario. import { throttle } from "lodash-es" no tiene efectos secundarios — solo hace disponible la función throttle. Los polyfills (import "core-js/stable") tienen efectos secundarios — modifican prototipos globales.
Para tus propios módulos, se recomienda: extraer estilos y polyfills en puntos de entrada separados, separar utilidades puras (funciones sin efectos secundarios) de módulos con efectos secundarios (inicialización, registro, registro de Service Worker). En el package.json del proyecto de nivel superior, especificar "sideEffects": false solo si todos los módulos son puros. Si hay estilos, especificar "sideEffects": ["*.css"] con precisión.
{
"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 los archivos CSS tienen efectos secundarios (no se pueden eliminar) y polyfills.js también. Todos los demás archivos JS en el paquete son puros — se pueden sacudir de forma segura. El campo module especifica la ruta a la versión ES del paquete que el bundler debe usar en lugar de la versión CommonJS (main) para Tree Shaking.
React Native con Metro Bundler admite una versión limitada de Tree Shaking. Metro no realiza un análisis estático completo de las exportaciones utilizadas (usedExports) como Webpack. En su lugar, Metro se basa en Terser para eliminar partes no utilizadas de los módulos durante la minificación. La efectividad de este enfoque es menor que la de Tree Shaking completo en Webpack.
Para una optimización máxima de los proyectos de React Native, se recomienda: usar bibliotecas con módulos ES (campo module en package.json), agregar babel-plugin-transform-remove-console para eliminar código de depuración y configurar Metro transformer.minifierConfig para Terser. Además, Ram Bundle (división del bundle en módulos) reduce la carga de pantallas no utilizadas.
Preguntas frecuentes
CommonJS (require/module.exports) no admite análisis estático — require puede llamarse dinámicamente dentro de condiciones y funciones. El bundler no puede determinar qué partes del módulo se utilizan realmente. Solo los módulos ES con import/export estático permiten Tree Shaking.
TypeScript es totalmente compatible con Tree Shaking siempre que tsconfig.json esté configurado para módulos ES: "module": "esnext". El compilador de TypeScript debe conservar import/export sin convertirlos a CommonJS. Babel con @babel/preset-typescript y modules: false también pasa correctamente los módulos ES a Webpack.
Webpack Bundle Analyzer es un plugin que visualiza la composición del bundle como un diagrama interactivo. Si una biblioteca está presente en el bundle pero sus funciones no se utilizan, Tree Shaking no funcionó. También puedes analizar el archivo de salida: busca exportaciones no utilizadas en el texto del bundle mediante grep.
Lodash v4 se distribuye como un paquete CommonJS. Para Tree Shaking, debes usar lodash-es, la versión ES de la biblioteca. Reemplaza import throttle from "lodash/throttle" por import { throttle } from "lodash-es" y configura resolve.alias en Webpack para reemplazar lodash por lodash-es.
Tree Shaking aumenta ligeramente el tiempo de compilación (entre un 5 y un 15%) porque agrega una etapa de análisis del grafo de dependencias y marcado de exportaciones utilizadas. En modo development, Tree Shaking generalmente está desactivado por velocidad. En producción, el tiempo adicional se justifica por una reducción significativa del tamaño del bundle.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también