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

Минификация CSS — процесс удаления из CSS-файлов всего лишнего: пробелов, переносов строк, комментариев, дублирующихся конструкций и других элементов, не влияющих на работу стилей. После минификации итоговый CSS становится значительно меньше, что уменьшает размер сборки и ускоряет загрузку сайта.

В экосистеме Webpack за минификацию CSS отвечает плагин Webpack CssMinimizerWebpackPlugin. Он появился как современная замена устаревшим решениям вроде optimize-css-assets-webpack-plugin.

Основные задачи плагина:

  • минимизация CSS;
  • удаление неиспользуемых пробелов;
  • сокращение значений;
  • оптимизация CSS-выражений;
  • сжатие итоговых файлов;
  • интеграция с production-сборкой Webpack.

Плагин работает поверх библиотеки cssnano, которая выполняет реальную оптимизацию CSS.


Установка

Для работы необходимо установить сам плагин и webpack:

npm install --save-dev css-minimizer-webpack-plugin

Обычно плагин используется вместе с:

  • mini-css-extract-plugin
  • css-loader
  • style-loader

Базовая настройка

Простейшая конфигурация:

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

module.exports = {
    mode: 'production',

    optimization: {
        minimizer: [
            `...`,
            new CssMinimizerPlugin(),
        ],
    },
};

Почему используется ...

Конструкция:

minimizer: [
    `...`,
    new CssMinimizerPlugin(),
]

имеет важное значение.

Webpack по умолчанию в production-режиме уже использует JavaScript-минимизатор:

TerserWebpackPlugin

Если полностью переопределить массив minimizer, стандартный JS-минимизатор исчезнет.

Неправильно:

optimization: {
    minimizer: [
        new CssMinimizerPlugin(),
    ],
}

В этом случае CSS будет минифицироваться, а JavaScript — нет.

Правильно:

optimization: {
    minimizer: [
        `...`,
        new CssMinimizerPlugin(),
    ],
}

... сохраняет стандартные minimizer-плагины Webpack.


Как работает минификация CSS

Исходный CSS:

body {
    margin: 0;
    padding: 0;
}

.container {
    color: red;
    color: red;
}

После минификации:

body{margin:0;padding:0}.container{color:red}

Удаляются:

  • лишние пробелы;
  • переносы строк;
  • повторяющиеся свойства;
  • ненужные символы;
  • комментарии.

Полная конфигурация CSS-сборки

Часто CssMinimizerWebpackPlugin используется вместе с извлечением CSS в отдельные файлы.

Установка зависимостей

npm install --save-dev \
mini-css-extract-plugin \
css-loader \
css-minimizer-webpack-plugin

Конфигурация webpack.config.js

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

module.exports = {
    mode: 'production',

    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                ],
            },
        ],
    },

    plugins: [
        new MiniCssExtractPlugin({
            filename: 'css/main.[contenthash].css',
        }),
    ],

    optimization: {
        minimizer: [
            `...`,
            new CssMinimizerPlugin(),
        ],
    },
};

Что происходит в этой конфигурации

MiniCssExtractPlugin

Извлекает CSS из JavaScript-бандла в отдельный файл.

Без него CSS может внедряться прямо в <style> через JavaScript.


css-loader

Позволяет импортировать CSS:

import './styles.css';

CssMinimizerPlugin

Минифицирует итоговый CSS после сборки.


Работа только в production

Минификация обычно нужна только в production-сборке.

Часто используется условная конфигурация:

const isProd = process.env.NODE_ENV === 'production';

Пример:

optimization: {
    minimizer: isProd
        ? [
            `...`,
            new CssMinimizerPlugin(),
        ]
        : [],
},

Использование source maps

Минифицированный CSS сложно читать. Для отладки используются source maps.

Конфигурация:

module.exports = {
    devtool: 'source-map',

    optimization: {
        minimizer: [
            `...`,
            new CssMinimizerPlugin(),
        ],
    },
};

Поддерживаемые devtool

Плагин поддерживает не все source map режимы.

Рекомендуемые варианты:

devtool: 'source-map'

или:

devtool: 'inline-source-map'

Настройка minimizerOptions

Плагин позволяет передавать параметры в cssnano.

Пример:

new CssMinimizerPlugin({
    minimizerOptions: {
        preset: [
            'default',
            {
                discardComments: {
                    removeAll: true,
                },
            },
        ],
    },
})

Удаление комментариев

Исходный CSS:

/* Main styles */

body {
    margin: 0;
}

После сборки:

body{margin:0}

Комментарии полностью удаляются.


Использование preset

cssnano использует presets — наборы оптимизаций.

Наиболее популярный:

preset: 'default'

Пример:

new CssMinimizerPlugin({
    minimizerOptions: {
        preset: 'default',
    },
})

Отключение отдельных оптимизаций

Некоторые CSS-оптимизации могут быть нежелательными.

Например:

new CssMinimizerPlugin({
    minimizerOptions: {
        preset: [
            'default',
            {
                mergeLonghand: false,
            },
        ],
    },
})

mergeLonghand

Опция объединяет CSS-свойства.

Исходный CSS:

padding-top: 10px;
padding-right: 20px;
padding-bottom: 10px;
padding-left: 20px;

После оптимизации:

padding:10px 20px

Иногда подобное преобразование может ломать логику CSS.


parallel

Минификация CSS может быть тяжёлой операцией для больших проектов.

Плагин поддерживает параллельную обработку:

new CssMinimizerPlugin({
    parallel: true,
})

Принцип parallel

Webpack создаёт несколько worker-процессов.

Каждый worker:

  • получает CSS-файл;
  • минифицирует его;
  • возвращает результат.

Это ускоряет production-сборку.


Ограничение количества потоков

Можно указать число worker-процессов:

new CssMinimizerPlugin({
    parallel: 4,
})

exclude и include

Можно ограничивать набор файлов для минификации.

Пример:

new CssMinimizerPlugin({
    exclude: /vendor/,
})

Минификация только определённых файлов

new CssMinimizerPlugin({
    include: /\.min\.css$/,
})

test

Определяет, какие файлы должны минифицироваться.

new CssMinimizerPlugin({
    test: /\.css$/i,
})

Минификация CSS из SCSS

Плагин работает не только с обычным CSS.

Пример цепочки:

npm install --save-dev \
sass \
sass-loader \
css-loader \
mini-css-extract-plugin \
css-minimizer-webpack-plugin

Конфигурация

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

module.exports = {
    mode: 'production',

    module: {
        rules: [
            {
                test: /\.scss$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                    'sass-loader',
                ],
            },
        ],
    },

    plugins: [
        new MiniCssExtractPlugin(),
    ],

    optimization: {
        minimizer: [
            `...`,
            new CssMinimizerPlugin(),
        ],
    },
};

Последовательность обработки

SCSS проходит несколько этапов:

SCSS
↓
sass-loader
↓
css-loader
↓
MiniCssExtractPlugin
↓
CssMinimizerPlugin
↓
Готовый CSS

Использование с PostCSS

Часто CSS проходит через PostCSS перед минификацией.

Пример:

use: [
    MiniCssExtractPlugin.loader,
    'css-loader',
    'postcss-loader',
]

После PostCSS CSS попадает в CssMinimizerPlugin.


Разница между PostCSS и CssMinimizerPlugin

PostCSS

Преобразует CSS:

  • autoprefixer;
  • preset-env;
  • плагины PostCSS;
  • полифилы.

CssMinimizerPlugin

Отвечает только за оптимизацию и сжатие.


Оптимизация media queries

Плагин способен:

  • удалять дубликаты;
  • оптимизировать media queries;
  • сокращать записи.

Исходный код:

@media screen and (max-width: 768px) {
    .box {
        margin: 0px;
    }
}

После минификации:

@media screen and (max-width:768px){.box{margin:0}}

Сжатие цветов

Исходный CSS:

color: #ffffff;

После минификации:

color:#fff

Оптимизация нулевых значений

Исходный CSS:

margin: 0px;
padding: 0em;

После:

margin:0;padding:0

Удаление дубликатов

Исходный CSS:

.button {
    color: red;
    color: red;
}

После:

.button{color:red}

Работа с CSS Variables

Плагин корректно работает с CSS Custom Properties.

Исходный CSS:

:root {
    --main-color: red;
}

После минификации:

:root{--main-color:red}

Использование с CSS Modules

Плагин совместим с CSS Modules.

Пример:

{
    test: /\.module\.css$/,
    use: [
        MiniCssExtractPlugin.loader,
        {
            loader: 'css-loader',
            options: {
                modules: true,
            },
        },
    ],
}

Минификация применяется уже после генерации локальных классов.


Производительность

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

  • увеличивает время production-сборки;
  • уменьшает размер файлов;
  • улучшает загрузку сайта.

На крупных проектах экономия может составлять сотни килобайт.


Типичная production-конфигурация

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

module.exports = {
    mode: 'production',

    devtool: 'source-map',

    module: {
        rules: [
            {
                test: /\.css$/,
                use: [
                    MiniCssExtractPlugin.loader,
                    'css-loader',
                ],
            },
        ],
    },

    plugins: [
        new MiniCssExtractPlugin({
            filename: 'css/[name].[contenthash].css',
        }),
    ],

    optimization: {
        minimize: true,

        minimizer: [
            `...`,

            new CssMinimizerPlugin({
                parallel: true,

                minimizerOptions: {
                    preset: [
                        'default',
                        {
                            discardComments: {
                                removeAll: true,
                            },
                        },
                    ],
                },
            }),
        ],
    },
};

Типичные ошибки

Отсутствует ...

minimizer: [
    new CssMinimizerPlugin(),
]

Ломает JS-минимизацию.


Использование style-loader в production

use: [
    'style-loader',
    'css-loader',
]

CSS внедряется через JavaScript вместо отдельного файла.


Отсутствует production mode

mode: 'development'

Минификация может работать некорректно или отсутствовать.


Неверный порядок loader’ов

Неправильно:

use: [
    'css-loader',
    MiniCssExtractPlugin.loader,
]

Правильно:

use: [
    MiniCssExtractPlugin.loader,
    'css-loader',
]

Сравнение style-loader и MiniCssExtractPlugin

style-loader

  • внедряет CSS в DOM;
  • удобен для development;
  • не создаёт отдельный CSS-файл.

MiniCssExtractPlugin

  • создаёт отдельный CSS;
  • подходит для production;
  • работает вместе с минификацией.

Когда минификация особенно важна

Минификация CSS критически важна при:

  • большом количестве компонентов;
  • использовании UI-библиотек;
  • подключении Bootstrap;
  • генерации CSS из SCSS;
  • Tailwind CSS;
  • крупных SPA-приложениях.

Взаимодействие с кэшированием

Минифицированные CSS-файлы часто используют:

filename: '[name].[contenthash].css'

Это позволяет:

  • эффективно кэшировать CSS;
  • обновлять файлы только при изменениях;
  • уменьшать количество повторных загрузок.

Размер CSS до и после минификации

Пример:

Состояние Размер
Исходный CSS 420 KB
После минификации 280 KB
После gzip 45 KB

gzip и минификация

Минификация и gzip — разные процессы.

Минификация

Удаляет лишние символы.


gzip

Архивирует данные при передаче по сети.

Обычно используются вместе.


Совместимость

CssMinimizerWebpackPlugin:

  • поддерживает Webpack 5;
  • работает с PostCSS;
  • совместим с Sass;
  • поддерживает CSS Modules;
  • интегрируется с source maps;
  • поддерживает многопоточность.