В реальных проектах конфигурация сборки редко ограничивается одним файлом. Разные условия работы требуют разных настроек: режим разработки, продакшн-сборка, тестовая среда, отдельные сборки для библиотек или админ-панели. В результате появляется необходимость в нескольких конфигурационных файлах, которые управляются как единая система.
Наиболее распространённый подход — разделение конфигурации на базовую и окруженческие части.
Структура проекта:
webpack/
webpack.common.js
webpack.dev.js
webpack.prod.js
webpack.config.js
Базовый файл содержит общие настройки, которые не зависят от окружения: входные точки, правила обработки модулей, базовые плагины.
Общий конфиг обычно включает:
Пример:
// webpack.common.js
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, '../dist'),
filename: 'bundle.js',
clean: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
}
]
},
resolve: {
extensions: ['.js', '.json'],
alias: {
'@': path.resolve(__dirname, '../src')
}
}
};
Этот файл не должен содержать логики, зависящей от режима сборки. Его задача — описать структуру проекта.
Файл разработки расширяет базовую конфигурацию и добавляет инструменты, ускоряющие процесс разработки.
Ключевые особенности:
// webpack.dev.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
module.exports = merge(common, {
mode: 'development',
devtool: 'eval-source-map',
devServer: {
static: './dist',
hot: true,
port: 3000,
open: true
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
});
Главная цель этой конфигурации — скорость итераций, а не оптимизация результата.
Продакшн-конфигурация ориентирована на минимальный размер и максимальную производительность.
Типичные задачи:
// webpack.prod.js
const { merge } = require('webpack-merge');
const common = require('./webpack.common');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = merge(common, {
mode: 'production',
devtool: 'source-map',
output: {
filename: '[name].[contenthash].js'
},
module: {
rules: [
{
test: /\.css$/,
use: [MiniCssExtractPlugin.loader, 'css-loader']
}
]
},
plugins: [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})
],
optimization: {
splitChunks: {
chunks: 'all'
}
}
});
Здесь появляется хеширование файлов, позволяющее эффективно использовать кэширование в браузере.
Пакет webpack-merge используется для корректного объединения объектов конфигурации.
Особенность Webpack в том, что простое объединение через spread-операторы может привести к перезаписи сложных структур, таких как module.rules или plugins. webpack-merge решает эту проблему, глубоко объединяя структуры.
Установка:
npm install webpack-merge
Принцип работы:
const { merge } = require('webpack-merge');
const result = merge(configA, configB);
При конфликте правил loaders не затираются, а дополняются.
Webpack позволяет явно указывать используемый конфигурационный файл:
webpack --config webpack/webpack.prod.js
Это ключевой механизм переключения между режимами без изменения кода.
В package.json обычно описываются скрипты:
{
"scripts": {
"dev": "webpack serve --config webpack/webpack.dev.js",
"build": "webpack --config webpack/webpack.prod.js"
}
}
Такой подход делает процесс сборки предсказуемым и изолированным.
Webpack поддерживает экспорт функции вместо объекта. Это позволяет динамически формировать конфигурацию.
module.exports = (env, argv) => {
const isProd = argv.mode === 'production';
return {
mode: isProd ? 'production' : 'development',
entry: './src/index.js',
output: {
filename: isProd ? '[name].[contenthash].js' : '[name].js'
}
};
};
Такой подход уменьшает необходимость в нескольких файлах, но при усложнении логики часто снова приводит к разделению конфигураций.
Передача параметров через CLI:
webpack --env target=legacy
Использование в конфигурации:
module.exports = (env) => {
return {
entry: env.target === 'legacy'
? './src/legacy.js'
: './src/index.js'
};
};
Это позволяет создавать вариативные сборки без увеличения числа файлов.
Webpack поддерживает экспорт массива конфигураций. Каждая конфигурация собирается отдельно.
module.exports = [
{
name: 'client',
entry: './src/client.js',
output: {
filename: 'client.bundle.js'
}
},
{
name: 'admin',
entry: './src/admin.js',
output: {
filename: 'admin.bundle.js'
}
}
];
Это используется для:
Каждая конфигурация имеет собственный граф зависимостей и независимый результат.
В крупных проектах конфигурации часто делятся не только по окружениям, но и по назначению:
Пример структуры:
webpack/
webpack.client.js
webpack.server.js
webpack.lib.js
SSR конфигурация:
module.exports = {
target: 'node',
entry: './src/server/index.js',
output: {
filename: 'server.js',
libraryTarget: 'commonjs2'
}
};
Для уменьшения дублирования часто используют фабрики конфигураций.
const createConfig = ({ mode, target }) => ({
mode,
target,
entry: './src/index.js',
output: {
filename: target === 'node' ? 'server.js' : 'client.js'
}
});
module.exports = [
createConfig({ mode: 'production', target: 'web' }),
createConfig({ mode: 'production', target: 'node' })
];
Такой подход делает конфигурации предсказуемыми и легко расширяемыми.
При разделении конфигураций важно учитывать:
Ошибки в этих областях приводят к перезаписи артефактов или некорректной сборке.
Webpack выполняет массив конфигураций параллельно, что ускоряет процесс при многочастной архитектуре.
Однако это увеличивает нагрузку на CPU и память, особенно при использовании TypeScript, Babel и сложных loader цепочек.
Оптимизация включает:
В монорепозиториях конфигурации часто привязаны к пакетам:
packages/
app/
webpack.config.js
admin/
webpack.config.js
shared/
Общие настройки выносятся в корневой пакет:
webpack.base.js
И импортируются локально:
const base = require('../. ./webpack.base');
module.exports = {
...base,
entry: './src/index.js'
};
Разделение конфигурации позволяет добиться:
При этом чрезмерное дробление конфигураций усложняет сопровождение, поэтому баланс между общими и специфическими настройками становится ключевым архитектурным решением.