Webpack 5 sigue siendo el bundler dominante para aplicaciones de produccion a gran escala. Esta guia cubre desde loaders basicos hasta tecnicas de optimizacion avanzadas.
Conceptos basicos de Webpack 5 y entry/output
El archivo webpack.config.js controla todo el comportamiento de bundling.
// webpack.config.js — Complete Production Config
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const isDev = process.env.NODE_ENV !== 'production';
module.exports = {
mode: isDev ? 'development' : 'production',
entry: {
main: './src/index.tsx',
// Multiple entry points for different pages
// admin: './src/admin/index.tsx',
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: isDev ? '[name].js' : '[name].[contenthash:8].js',
chunkFilename: isDev ? '[name].chunk.js' : '[name].[contenthash:8].chunk.js',
assetModuleFilename: 'assets/[hash:8][ext][query]',
clean: true, // Remove old files on each build
publicPath: '/',
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.jsx', '.json'],
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
},
},
// Persistent filesystem cache — huge speed improvement for incremental builds
cache: {
type: 'filesystem',
buildDependencies: {
config: [__filename], // Invalidate cache when config changes
},
},
devServer: {
port: 3000,
hot: true,
historyApiFallback: true,
compress: true,
},
};Loaders esenciales
Los loaders transforman archivos antes de agregarlos al grafo de dependencias.
// Essential Webpack Loaders Configuration
module.exports = {
module: {
rules: [
// 1. TypeScript/JavaScript with Babel
{
test: /\.(ts|tsx|js|jsx)$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: [
['@babel/preset-env', { targets: '>0.25%, not dead' }],
['@babel/preset-react', { runtime: 'automatic' }],
'@babel/preset-typescript',
],
// Cache Babel results for faster rebuilds
cacheDirectory: true,
cacheCompression: false,
},
},
},
// 2. CSS / SCSS / PostCSS
{
test: /\.module\.(css|scss)$/, // CSS Modules
use: [
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: { modules: true, importLoaders: 2 },
},
'postcss-loader',
'sass-loader',
],
},
{
test: /(?!\.module)\.(css|scss)$/, // Global CSS
use: [
isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
'css-loader',
'postcss-loader',
'sass-loader',
],
},
// 3. Images and fonts (Webpack 5 Asset Modules — no loader needed)
{
test: /\.(png|jpg|jpeg|gif|webp)$/i,
type: 'asset', // Auto-chooses between inline and resource
parser: {
dataUrlCondition: { maxSize: 10 * 1024 }, // Inline if < 10KB
},
},
{
test: /\.svg$/,
use: ['@svgr/webpack'], // Import SVGs as React components
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
],
},
};Code splitting y carga diferida
El code splitting es la caracteristica de optimizacion mas importante de Webpack.
// Code Splitting and Lazy Loading
// 1. Dynamic imports (automatic code splitting)
// React.lazy creates a separate chunk for each lazy component
import React, { lazy, Suspense } from 'react';
import { Routes, Route } from 'react-router-dom';
const Dashboard = lazy(() => import('./pages/Dashboard'));
const Settings = lazy(() => import('./pages/Settings'));
const Analytics = lazy(() => import('./pages/Analytics'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/settings" element={<Settings />} />
<Route path="/analytics" element={<Analytics />} />
</Routes>
</Suspense>
);
}
// 2. Manual chunk naming with magic comments
const HeavyChart = lazy(
() => import(/* webpackChunkName: "charts" */ './components/HeavyChart')
);
// 3. Webpack SplitChunksPlugin configuration
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
// Vendor chunk: node_modules
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
priority: 20,
},
// Separate React from other vendors
react: {
test: /[\\/]node_modules[\\/](react|react-dom|react-router)[\\/]/,
name: 'react-vendor',
chunks: 'all',
priority: 30,
},
// Shared chunks used by 2+ modules
common: {
name: 'common',
minChunks: 2,
priority: 10,
reuseExistingChunk: true,
},
},
},
runtimeChunk: 'single', // Separate runtime chunk
},
};Optimizacion de produccion
Las builds de produccion requieren optimizacion agresiva.
// Production Optimization Configuration
module.exports = {
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
parallel: true, // Use all CPU cores
terserOptions: {
compress: {
drop_console: true, // Remove console.log in production
drop_debugger: true,
pure_funcs: ['console.log'],
},
format: { comments: false },
},
}),
new CssMinimizerPlugin(),
],
// Tree shaking — only include used exports
usedExports: true,
sideEffects: true, // Respect package.json sideEffects field
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash:8].css',
}),
new HtmlWebpackPlugin({
template: './public/index.html',
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true,
},
}),
// Only add analyzer when needed: ANALYZE=true npm run build
process.env.ANALYZE && new BundleAnalyzerPlugin(),
].filter(Boolean),
};Preguntas frecuentes
Cuando usar Webpack en lugar de Vite?
Elige Webpack para Module Federation, grandes codebases existentes o soporte IE11.
Como analizar el tamano del bundle Webpack?
Usa webpack-bundle-analyzer para visualizar que consume el tamano del bundle.
Que es Module Federation en Webpack 5?
Module Federation permite que multiples builds de Webpack compartan codigo en tiempo de ejecucion.
Por que mi build de Webpack es tan lenta?
Causas comunes: sin cache persistente, demasiados loaders, babel sin excluir node_modules.