Optimizaciones Integradas en el Modo de Producción
Eliminación de Código Muerto (Tree Shaking)
Tree shaking es un proceso que elimina código JavaScript no utilizado durante el empaquetado. Aprovecha la estructura estática del sistema de módulos ES6 con las declaraciones import y export.
Cuando se importa un módulo, solo las funcionalidades realmente empleadas en la aplicación final se incluyen en el bundle de producción. Las partes no referenciadas se descartan automáticamente.
Concatenación de Ámbitos (Scope Hoisting)
Esta técnica analiza las dependencias entre módulos y los fusiona en un solo ámbito cuando es posible, reduciendo el tamaño del bundle y acelerando la ejecución.
Para que funcione correctamente, los módulos deben utilizar la sintaxis de módulos ES6. La concatenación solo se aplica a módulos importados una única vez, evitando así duplicación de código.
Minificación del Código
En modo producción, todo el código JavaScript se procesa automáticamente mediante herramientas de minificación que eliminan espacios, comentarios y acortan nombres de variables.
Optimización de CSS
Extracción de Archivos CSS Independientes
El plugin mini-css-extract-plugin permite generar archivos CSS separados en lugar de inyectar estilos mediante JavaScript. Esto mejora el rendimiento de carga y habilita la carga parcial.
Configuración básica:
const MiniCssExtract = require('mini-css-extract-plugin');
module.exports = {
plugins: [
new MiniCssExtract({
filename: 'styles/[name].[hash].css'
})
],
module: {
rules: [{
test: /\.scss$/,
use: [
MiniCssExtract.loader,
'css-loader',
'sass-loader'
]
}]
}
};
Prefijos CSS Automáticos
La integración con postcss y autoprefixer añade prefijos de navegador necesarios automáticamente. Se configura mediante un archivo postcss.config.js:
module.exports = {
plugins: [
require('autoprefixer')({
grid: true
})
]
};
Minificación de CSS
Para comprimir CSS en producción, se combina optimize-css-assets-webpack-plugin con terser-webpack-plugin:
const Terser = require('terser-webpack-plugin');
const CssOptimize = require('optimize-css-assets-webpack-plugin');
module.exports = {
optimization: {
minimizer: [
new Terser(),
new CssOptimize()
]
}
};
Separación de Código (Code Splitting)
Configuración de Múltiples Puntos de Entrada
Webpack permite definir varios puntos de entrada para crear bundles separados:
module.exports = {
entry: {
app: './src/index.js',
admin: './src/admin.js'
},
output: {
filename: '[name].[contenthash].js',
path: __dirname + '/dist'
}
};
El problema de esta aproximación es que los módulos compartidos entre entradas se duplican en cada bundle.
Extracción de Código Común
La configuración de splitChunks optimiza la separación de código compartido:
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
minSize: 20000,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
name: 'vendor',
chunks: 'initial'
},
commons: {
minChunks: 2,
name: 'commons',
reuseExistingChunk: true
}
}
}
}
};
Importaciones Dinámicas (Lazy Loading)
Las importaciones dinámicas permiten cargar módulos bajo demanda, mejorando el rendimiento inicial de la aplicación:
async function loadChartLibrary() {
const { Chart } = await import(/* webpackChunkName: "chart" */ 'chart.js');
return Chart;
}
// Uso condicional
if (needsChart) {
const ChartLib = await loadChartLibrary();
// Inicializar gráfico
}
Configuración Avanzada de SplitChunks
Parámetros principales para controlar la división:
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 250000,
minChunks: 1,
maxAsyncRequests: 6,
maxInitialRequests: 4,
automaticNameDelimiter: '~',
name: true,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
Optimizaciones de Rendimiento
Omitir Análisis de Dependencias
Para librerías grandes sin dependencias internas, se puede desactivar el análisis:
module.exports = {
module: {
noParse: /^(jquery|lodash)$/
}
};
Ignorar Módulos Específicos
Para reducir el tamaño de paquetes como moment.js:
const webpack = require('webpack');
module.exports = {
plugins: [
new webpack.IgnorePlugin({
resourceRegExp: /^\.\/locale$/,
contextRegExp: /moment$/
})
]
};
// Importación manual del locale necesario
import moment from 'moment';
import 'moment/locale/es';
moment.locale('es');
DLL para Librerías Estátiles
Configuración del DLL
Archivo webpack.dll.js para precompilar dependencias:
const path = require('path');
const { DllPlugin } = require('webpack');
module.exports = {
mode: 'production',
entry: {
react: ['react', 'react-dom', 'react-router']
},
output: {
path: path.resolve(__dirname, 'dll'),
filename: '[name]_bundle.js',
library: '[name]_lib'
},
plugins: [
new DllPlugin({
name: '[name]_lib',
path: path.join(__dirname, 'dll', '[name]_manifest.json')
})
]
};
Referencia en la Configuración Principal
const { DllReferencePlugin } = require('webpack');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
module.exports = {
plugins: [
new DllReferencePlugin({
manifest: require('./dll/react_manifest.json')
}),
new AddAssetHtmlPlugin({
filepath: path.resolve(__dirname, 'dll/react_bundle.js')
})
]
};
Explotación de la Caché del Navegador
Para garantizar la invalidación de caché entre versiones:
output: {
filename: '[name].[contenthash:12].js',
chunkFilename: '[name].[contenthash:8].chunk.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/'
}
Los hashes basados en contenido cambian solo cuando el archivo se modifica, permitiendo que dependencias no modificadas permanezcan en caché.
Aálisis de Bundle
Generar estadísticas para análisis:
npx webpack --profile --json > analysis.json
Herramientas recomendadas para visualización:
webpack-bundle-analyzer- Integración directa con webpacksource-map-explorer- Análisis de mapas de código fuente- Paneles de cobertura en Chrome DevTools
Precarga y Carga Diferida
Indicaciones mágicas para control de prioridad de carga:
import(/* webpackPrefetch: true */ 'ChartModule');
// Carga diferida - solo cuando se necesite
const heavyModule = await import(
/* webpackChunkName: "heavy" */
/* webpackMode: "lazy" */
'./heavyModule'
);
webpackPrefetch descarga el recurso en segundo plano durante tiempos de inactividad, mientras que webpackPreload lo carga inmediatamente con el chunk padre.