Webpack Core Concepts and Configuration Guide

Deep dive into Webpack bundling principles, core configuration, plugin system and optimization

Webpack Core Concepts and Configuration Guide

Webpack is the most popular JavaScript module bundler. This article explores its core concepts and configuration methods.

Core Concepts

Basic Architecture

Webpack Core Concepts:
┌─────────────────────────────────────────────────────┐
│                                                     │
│   Entry                                             │
│   └── Starting point for bundling                  │
│                                                     │
│   Output                                            │
│   └── Location and naming of bundled files         │
│                                                     │
│   Loaders                                           │
│   └── Transform non-JS files (CSS, images, etc.)   │
│                                                     │
│   Plugins                                           │
│   └── Extend functionality (compression, etc.)     │
│                                                     │
│   Mode                                              │
│   └── development / production / none              │
│                                                     │
└─────────────────────────────────────────────────────┘

Basic Configuration

// webpack.config.js
const path = require('path');

module.exports = {
  // Mode
  mode: 'development',

  // Entry
  entry: './src/index.js',

  // Output
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'bundle.js',
    clean: true  // Clean dist on each build
  },

  // Module rules
  module: {
    rules: []
  },

  // Plugins
  plugins: [],

  // Dev server
  devServer: {
    static: './dist',
    port: 3000,
    hot: true
  }
};

Loaders Configuration

Processing JavaScript

module.exports = {
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: [
              ['@babel/preset-env', {
                targets: '> 0.25%, not dead',
                useBuiltIns: 'usage',
                corejs: 3
              }]
            ]
          }
        }
      }
    ]
  }
};

Processing TypeScript

module.exports = {
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx']
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/
      }
    ]
  }
};

Processing CSS

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  module: {
    rules: [
      // Development - style-loader injects into DOM
      {
        test: /\.css$/,
        use: ['style-loader', 'css-loader']
      },
      // Production - extract to separate file
      {
        test: /\.css$/,
        use: [MiniCssExtractPlugin.loader, 'css-loader']
      },
      // Sass/SCSS
      {
        test: /\.s[ac]ss$/,
        use: [
          'style-loader',
          'css-loader',
          'sass-loader'
        ]
      },
      // CSS Modules
      {
        test: /\.module\.css$/,
        use: [
          'style-loader',
          {
            loader: 'css-loader',
            options: {
              modules: {
                localIdentName: '[name]__[local]--[hash:base64:5]'
              }
            }
          }
        ]
      }
    ]
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: 'styles/[name].[contenthash].css'
    })
  ]
};

Processing Assets

module.exports = {
  module: {
    rules: [
      // Images
      {
        test: /\.(png|jpg|jpeg|gif|svg)$/i,
        type: 'asset',
        parser: {
          dataUrlCondition: {
            maxSize: 8 * 1024  // Under 8KB to base64
          }
        },
        generator: {
          filename: 'images/[name].[hash:8][ext]'
        }
      },
      // Fonts
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
        generator: {
          filename: 'fonts/[name].[hash:8][ext]'
        }
      }
    ]
  }
};

Common Plugins

HTML Generation

const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      filename: 'index.html',
      inject: 'body',
      minify: {
        removeComments: true,
        collapseWhitespace: true
      }
    })
  ]
};

Environment Variables

const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      'process.env.NODE_ENV': JSON.stringify('production'),
      'process.env.API_URL': JSON.stringify('https://api.example.com'),
      __DEV__: JSON.stringify(false)
    })
  ]
};

Copy Static Assets

const CopyPlugin = require('copy-webpack-plugin');

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        { from: 'public', to: '' },
        { from: 'src/assets', to: 'assets' }
      ]
    })
  ]
};

Code Splitting

Entry Splitting

module.exports = {
  entry: {
    main: './src/index.js',
    vendor: './src/vendor.js',
    admin: './src/admin/index.js'
  },
  output: {
    filename: '[name].[contenthash].js'
  }
};

Dynamic Imports

// Automatic code splitting
button.addEventListener('click', async () => {
  const module = await import(
    /* webpackChunkName: "heavy-module" */
    './heavy-module.js'
  );
  module.doSomething();
});

// Prefetch
import(
  /* webpackPrefetch: true */
  './analytics.js'
);

// Preload
import(
  /* webpackPreload: true */
  './critical-module.js'
);

SplitChunks Optimization

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 20000,
      minChunks: 1,
      maxAsyncRequests: 30,
      maxInitialRequests: 30,
      cacheGroups: {
        // Third-party libraries
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
          priority: 10
        },
        // React related
        react: {
          test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
          name: 'react',
          chunks: 'all',
          priority: 20
        },
        // Common code
        common: {
          minChunks: 2,
          name: 'common',
          chunks: 'all',
          priority: 5,
          reuseExistingChunk: true
        }
      }
    },
    runtimeChunk: 'single'
  }
};

Dev Server

DevServer Configuration

module.exports = {
  devServer: {
    static: {
      directory: path.join(__dirname, 'public')
    },
    port: 3000,
    hot: true,
    open: true,
    compress: true,
    historyApiFallback: true,

    // Proxy configuration
    proxy: [
      {
        context: ['/api'],
        target: 'http://localhost:8080',
        changeOrigin: true,
        pathRewrite: { '^/api': '' }
      }
    ],

    // Custom middleware
    setupMiddlewares: (middlewares, devServer) => {
      devServer.app.get('/health', (req, res) => {
        res.json({ status: 'ok' });
      });
      return middlewares;
    }
  }
};

Source Maps

module.exports = {
  // Development - fast rebuild, accurate line numbers
  devtool: 'eval-cheap-module-source-map',

  // Production - full mapping
  // devtool: 'source-map',

  // Production - hidden source
  // devtool: 'hidden-source-map',
};

Production Optimization

Compression Configuration

const TerserPlugin = require('terser-webpack-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = {
  mode: 'production',
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: {
            drop_console: true,
            drop_debugger: true
          },
          format: {
            comments: false
          }
        },
        extractComments: false
      }),
      new CssMinimizerPlugin()
    ]
  }
};

Tree Shaking

module.exports = {
  mode: 'production',
  optimization: {
    usedExports: true,
    sideEffects: true
  }
};

// package.json
{
  "sideEffects": [
    "*.css",
    "*.scss",
    "./src/polyfills.js"
  ]
}

Cache Optimization

module.exports = {
  output: {
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].chunk.js'
  },
  optimization: {
    moduleIds: 'deterministic',
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all'
        }
      }
    }
  }
};

Multi-Environment Config

Configuration Merging

// webpack.common.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    clean: true
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html'
    })
  ]
};

// webpack.dev.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'development',
  devtool: 'eval-cheap-module-source-map',
  output: {
    filename: '[name].js'
  },
  devServer: {
    port: 3000,
    hot: true
  }
});

// webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');

module.exports = merge(common, {
  mode: 'production',
  devtool: 'source-map',
  output: {
    filename: '[name].[contenthash].js'
  }
});

Best Practices Summary

Webpack Optimization Strategies:
┌─────────────────────────────────────────────────────┐
│                                                     │
│   Build Speed                                       │
│   ├── Narrow loader scope                          │
│   ├── Use persistent caching                       │
│   ├── Multi-process parallel builds                │
│   └── Use alias and extensions wisely              │
│                                                     │
│   Output Optimization                               │
│   ├── Code splitting + lazy loading                │
│   ├── Tree shaking removes dead code               │
│   ├── Compress JS/CSS/images                       │
│   └── Use contenthash appropriately                │
│                                                     │
│   Developer Experience                              │
│   ├── Hot Module Replacement (HMR)                 │
│   ├── Source maps configuration                    │
│   └── Separate dev/prod environments              │
│                                                     │
└─────────────────────────────────────────────────────┘
ConceptPurpose
EntryDefine bundling entry point
OutputDefine output configuration
LoadersTransform non-JS resources
PluginsExtend build functionality
SplitChunksCode splitting optimization

Understanding Webpack is mastering the core of modern frontend engineering.