MiniCssExtractPlugin — плагин для Webpack,
предназначенный для извлечения CSS-кода из JavaScript-бандлов в
отдельные CSS-файлы. В production-сборках это один из ключевых
инструментов оптимизации фронтенд-приложений.
Без использования плагина CSS может внедряться в страницу через
JavaScript с помощью style-loader. Такой подход удобен во
время разработки, однако для production он имеет ряд недостатков:
MiniCssExtractPlugin решает эти проблемы, создавая
отдельные .css-файлы.
npm install mini-css-extract-plugin --save-dev
Минимальная конфигурация включает:
style-loader на
MiniCssExtractPlugin.loader;const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'js/main.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
module: {
rules: [
{
test: /\.css$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
],
},
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/styles.css',
}),
],
};
Когда JavaScript импортирует CSS:
import './styles/main.css';
Webpack:
css-loader;MiniCssExtractPlugin.loader;В результате:
dist/
js/
main.js
css/
styles.css
use: [
'style-loader',
'css-loader',
]
Поведение:
<style>;use: [
MiniCssExtractPlugin.loader,
'css-loader',
]
Поведение:
На практике часто используют:
style-loader в development;MiniCssExtractPlugin в production.const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const isDev = process.env.NODE_ENV === 'development';
module.exports = {
module: {
rules: [
{
test: /\.css$/,
use: [
isDev
? 'style-loader'
: MiniCssExtractPlugin.loader,
'css-loader',
],
},
],
},
plugins: [
...(!isDev
? [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash].css',
}),
]
: []),
],
};
Если приложение имеет несколько entry points:
entry: {
home: './src/home.js',
admin: './src/admin.js',
},
можно генерировать отдельный CSS для каждого бандла:
new MiniCssExtractPlugin({
filename: 'css/[name].css',
})
Результат:
dist/
css/
home.css
admin.css
Production-сборки обычно используют хеширование файлов:
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash].css',
})
Пример:
main.a7f3c1.css
contenthash меняется только при изменении содержимого
CSS.
Преимущества:
Плагин не ограничивается обычным CSS.
Для SCSS:
npm install sass sass-loader --save-dev
Конфигурация:
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader',
],
}
Импорт:
import './styles/app.scss';
npm install less less-loader --save-dev
{
test: /\.less$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'less-loader',
],
}
npm install stylus stylus-loader --save-dev
{
test: /\.styl$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'stylus-loader',
],
}
{
test: /\.module\.css$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
modules: true,
},
},
],
}
.title {
color: red;
}
import styles from './App.module.css';
element.className = styles.title;
Для production:
modules: {
localIdentName: '[hash:base64]',
}
Для development:
modules: {
localIdentName: '[path][name]__[local]',
}
При использовании динамического импорта:
import('./profile');
Webpack создаёт отдельный JS chunk.
MiniCssExtractPlugin также создаёт CSS для этого
чанка:
profile.js
profile.css
Это особенно важно для:
Для CSS-чанков используется отдельная настройка:
new MiniCssExtractPlugin({
filename: 'css/[name].css',
chunkFilename: 'css/[id].css',
})
или:
chunkFilename: 'css/[name].[contenthash].css',
Иногда появляется предупреждение:
Conflicting order between:
Причина — Webpack не может гарантировать одинаковый порядок подключения CSS-модулей.
Для отключения предупреждения:
new MiniCssExtractPlugin({
ignoreOrder: true,
})
Однако это не устраняет проблему порядка, а только скрывает предупреждение.
Проблема обычно возникает при:
Пример:
import './a.css';
import './b.css';
в одном файле и:
import './b.css';
import './a.css';
в другом.
Webpack не знает, какой порядок считать правильным.
Для минимизации конфликтов обычно:
Иногда CSS содержит ссылки на изображения:
background-image: url('../images/bg.png');
Если ресурсы загружаются некорректно, помогает настройка
publicPath.
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../',
},
}
src/
styles/
main.scss
images/
logo.png
dist/
css/
main.css
images/
logo.png
Если CSS лежит глубже, относительные пути должны корректироваться
через publicPath.
По умолчанию loader использует ES Modules.
При необходимости можно отключить:
{
loader: MiniCssExtractPlugin.loader,
options: {
esModule: false,
},
}
Обычно это требуется при совместимости со старыми конфигурациями.
Исторически HMR для CSS через MiniCssExtractPlugin
работал нестабильно.
Современный Webpack поддерживает HMR лучше, однако наиболее распространённая практика остаётся такой:
style-loader;MiniCssExtractPlugin.Для генерации source maps:
module.exports = {
devtool: 'source-map',
};
и:
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
MiniCssExtractPlugin не минифицирует CSS.
Для минификации используется
css-minimizer-webpack-plugin.
Установка:
npm install css-minimizer-webpack-plugin --save-dev
Конфигурация:
const CssMinimizerPlugin = require(
'css-minimizer-webpack-plugin'
);
module.exports = {
optimization: {
minimizer: [
'...',
new CssMinimizerPlugin(),
],
},
};
const path = require('path');
const MiniCssExtractPlugin = require(
'mini-css-extract-plugin'
);
const CssMinimizerPlugin = require(
'css-minimizer-webpack-plugin'
);
module.exports = {
mode: 'production',
entry: './src/index.js',
output: {
filename: 'js/[name].[contenthash].js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
module: {
rules: [
{
test: /\.(scss|css)$/,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
sourceMap: true,
},
},
],
},
],
},
optimization: {
minimizer: [
'...',
new CssMinimizerPlugin(),
],
},
plugins: [
new MiniCssExtractPlugin({
filename: 'css/[name].[contenthash].css',
chunkFilename:
'css/[name].[contenthash].css',
}),
],
devtool: 'source-map',
};
После выполнения:
npx webpack
Webpack:
При использовании:
new HtmlWebpackPlugin()
Webpack автоматически подключает CSS:
<link href="css/main.css" rel="stylesheet">
Ручное подключение обычно не требуется.
Неверно:
use: [
'style-loader',
MiniCssExtractPlugin.loader,
'css-loader',
]
Нужно использовать только один loader.
Неверно:
use: [
'css-loader',
MiniCssExtractPlugin.loader,
]
Правильно:
use: [
MiniCssExtractPlugin.loader,
'css-loader',
]
Webpack выполняет loaders справа налево.
MiniCssExtractPlugin не умеет самостоятельно
обрабатывать CSS.
Нужен css-loader.
Loader без плагина работать не будет.
Неверно:
use: [
MiniCssExtractPlugin.loader,
'css-loader',
]
без:
plugins: [
new MiniCssExtractPlugin(),
]
Использование MiniCssExtractPlugin даёт:
Для production Webpack-проектов плагин фактически является стандартом де-факто при работе со стилями.