Webpack Plugins

Зачем нужно

Plugins расширяют возможности Webpack на этапе сборки. Если loaders трансформируют отдельные файлы, то plugins работают с результатом сборки целиком: генерируют HTML, извлекают CSS в отдельные файлы, определяют переменные окружения, анализируют размер бандла.

Где используется

  • Генерация HTML с подключёнными бандлами
  • Извлечение CSS в отдельные файлы для production
  • Определение переменных окружения (API URL, версия)
  • Анализ размера бандла и оптимизация
  • Копирование статических файлов, очистка dist

HtmlWebpackPlugin

Генерирует HTML-файл и автоматически подключает бандлы:

npm install --save-dev html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',   // Шаблон
      filename: 'index.html',         // Имя выходного файла
      title: 'Моё приложение',        // <title>
      minify: {                        // Минификация в production
        collapseWhitespace: true,
        removeComments: true,
      },
      inject: 'body',                 // Куда вставить <script>
      favicon: './src/favicon.ico',    // Фавиконка
    }),
  ],
};
<!-- src/index.html — шаблон -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
  <div id="app"></div>
  <!-- Webpack автоматически вставит <script> сюда -->
</body>
</html>
// Несколько HTML-страниц (multi-page)
plugins: [
  new HtmlWebpackPlugin({
    template: './src/index.html',
    filename: 'index.html',
    chunks: ['app'],           // Только бандл app
  }),
  new HtmlWebpackPlugin({
    template: './src/admin.html',
    filename: 'admin.html',
    chunks: ['admin'],         // Только бандл admin
  }),
],

MiniCssExtractPlugin

Извлекает CSS в отдельные файлы (вместо <style> тегов):

npm install --save-dev mini-css-extract-plugin
const MiniCssExtractPlugin = require('mini-css-extract-plugin');

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          MiniCssExtractPlugin.loader,  // Вместо style-loader
          'css-loader',
        ],
      },
    ],
  },
  plugins: [
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css', // Имя выходного CSS
    }),
  ],
};
// Dev: style-loader (быстро, HMR работает)
// Prod: MiniCssExtractPlugin (отдельный файл, кэшируется)

const isDev = process.env.NODE_ENV === 'development';

{
  test: /\.css$/,
  use: [
    isDev ? 'style-loader' : MiniCssExtractPlugin.loader,
    'css-loader',
  ],
}

CssMinimizerPlugin

Минификация CSS:

npm install --save-dev css-minimizer-webpack-plugin
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = {
  optimization: {
    minimizer: [
      '...',  // Сохранить дефолтный JS-минификатор (Terser)
      new CssMinimizerPlugin,
    ],
  },
};

DefinePlugin

Определяет глобальные константы на этапе сборки (встроен в Webpack):

const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.DefinePlugin({
      // Значения подставляются КАК ЕСТЬ (как текст)
      'process.env.API_URL': JSON.stringify('https://api.example.com'),
      'process.env.NODE_ENV': JSON.stringify('production'),
      __DEV__: JSON.stringify(false),
      VERSION: JSON.stringify('1.0.0'),
    }),
  ],
};
// В коде приложения — константы заменяются при сборке
if (process.env.NODE_ENV === 'production') {
  // Этот if в production бандле станет: if ("production" === "production")
  // Tree shaking удалит else-ветку
  enableAnalytics;
}

console.log(`API: ${process.env.API_URL}`);
// В бандле: console.log(`API: ${"https://api.example.com"}`);

BundleAnalyzerPlugin

Визуализация содержимого бандла — помогает найти «тяжёлые» зависимости:

npm install --save-dev webpack-bundle-analyzer
const BundleAnalyzerPlugin =
  require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin({
      analyzerMode: 'static',         // HTML-отчёт
      openAnalyzer: false,             // Не открывать автоматически
      reportFilename: 'report.html',   // Имя файла отчёта
    }),
  ],
};
// Запуск только при анализе
// webpack.config.js
module.exports = (env) => ({
  plugins: [
    env.analyze && new BundleAnalyzerPlugin,
  ].filter(Boolean),
});

// npm run build -- --env analyze

CopyWebpackPlugin

Копирует статические файлы в dist:

npm install --save-dev copy-webpack-plugin
const CopyPlugin = require('copy-webpack-plugin');

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

ProvidePlugin

Автоматический import модулей (встроен в Webpack):

const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.ProvidePlugin({
      $: 'jquery',           // $ доступен везде без import
      React: 'react',        // React доступен в JSX файлах
    }),
  ],
};

EnvironmentPlugin

Упрощённый DefinePlugin для process.env (встроен в Webpack):

const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.EnvironmentPlugin({
      NODE_ENV: 'development',   // Значение по умолчанию
      API_URL: 'http://localhost:3000',
      DEBUG: false,
    }),
  ],
};

Комбинация плагинов — полный конфиг

const path = require('path');
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');

const isDev = process.env.NODE_ENV === 'development';

module.exports = {
  mode: isDev ? 'development' : 'production',
  entry: './src/index.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: isDev ? '[name].js' : '[name].[contenthash].js',
    clean: true,
  },

  plugins: [
    new HtmlWebpackPlugin({
      template: './src/index.html',
      minify: !isDev,
    }),

    !isDev && new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),

    new webpack.DefinePlugin({
      'process.env.API_URL': JSON.stringify(
        isDev ? 'http://localhost:4000' : 'https://api.prod.com'
      ),
    }),

    new CopyPlugin({
      patterns: [{ from: 'public', to: '.' }],
    }),
  ].filter(Boolean),

  optimization: {
    minimizer: [
      '...',
      !isDev && new CssMinimizerPlugin,
    ].filter(Boolean),
  },
};

Частые ошибки

  1. JSON.stringify забывают в DefinePlugin'production' вставится без кавычек → ошибка
  2. style-loader + MiniCssExtract одновременно — в dev используют style-loader, в prod — MiniCssExtract
  3. Нет contenthash — без хеша браузер кэширует старые файлы
  4. CopyPlugin для обрабатываемых файлов — CSS/JS/картинки проходят через loaders, а не копируются
  5. BundleAnalyzer в production — забывают отключить, сборка замедляется

Практика

  1. Настроить HtmlWebpackPlugin с шаблоном и мета-тегами
  2. Подключить MiniCssExtractPlugin для production
  3. Использовать DefinePlugin для API_URL в разных окружениях
  4. Проанализировать бандл с BundleAnalyzerPlugin и найти тяжёлую зависимость
  5. Настроить CopyPlugin для статических файлов (robots.txt, favicon)

Связанные темы

  • Webpack — общая настройка
  • Webpack loaders — трансформация файлов
  • Vite — альтернативный сборщик с встроенными плагинами

Ресурсы