Webpack поддерживает экспорт не только одного объекта конфигурации, но и массива конфигураций. Такой подход позволяет запускать несколько независимых сборок в рамках одного процесса Webpack. Каждая конфигурация обрабатывается отдельно, имеет собственные точки входа, плагины, правила загрузчиков, режимы сборки и выходные директории.
Экспорт массива особенно полезен при разработке сложных приложений, библиотек, мультиплатформенных проектов и систем с разделением клиентской и серверной логики.
Стандартный вариант выглядит следующим образом:
// webpack.config.js
module.exports = [
{
name: 'client',
entry: './src/client.js',
output: {
filename: 'client.bundle.js'
}
},
{
name: 'server',
entry: './src/server.js',
output: {
filename: 'server.bundle.js'
}
}
];
Webpack воспринимает каждый объект массива как самостоятельную конфигурацию.
Во время сборки:
npx webpack
Webpack последовательно обработает обе конфигурации.
При использовании массива рекомендуется задавать свойство
name.
{
name: 'client'
}
Поле name:
Пример:
npx webpack --config-name client
Webpack выполнит только конфигурацию с именем
client.
Один из наиболее распространённых сценариев — отдельная сборка frontend и backend.
const path = require('path');
module.exports = [
{
name: 'frontend',
target: 'web',
mode: 'production',
entry: './src/frontend/index.js',
output: {
path: path.resolve(__dirname, 'dist/frontend'),
filename: 'bundle.js'
}
},
{
name: 'backend',
target: 'node',
mode: 'production',
entry: './src/backend/index.js',
output: {
path: path.resolve(__dirname, 'dist/backend'),
filename: 'server.js'
}
}
];
Особенности:
target: 'web' собирает код для браузера;target: 'node' адаптирует сборку под Node.js;output.path;Каждый объект внутри массива полностью автономен.
Пример:
module.exports = [
{
name: 'modern',
entry: './src/index.js',
output: {
filename: 'modern.js'
},
experiments: {
outputModule: true
}
},
{
name: 'legacy',
entry: './src/index.js',
output: {
filename: 'legacy.js'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
}
]
}
}
];
В данном случае:
Webpack-массив часто применяется при генерации нескольких версий приложения:
module.exports = [
{
name: 'development',
mode: 'development',
devtool: 'eval-source-map',
entry: './src/index.js',
output: {
filename: 'app.dev.js'
}
},
{
name: 'production',
mode: 'production',
entry: './src/index.js',
output: {
filename: 'app.prod.js'
}
}
];
Такой подход позволяет:
Повторение одинаковых настроек быстро приводит к дублированию. Обычно создаётся базовая конфигурация.
const path = require('path');
const commonConfig = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist')
},
module: {
rules: [
{
test: /\.css$/,
use: ['style-loader', 'css-loader']
}
]
}
};
module.exports = [
{
...commonConfig,
name: 'development',
mode: 'development',
output: {
...commonConfig.output,
filename: 'dev.bundle.js'
}
},
{
...commonConfig,
name: 'production',
mode: 'production',
output: {
...commonConfig.output,
filename: 'prod.bundle.js'
}
}
];
Подобная структура:
При большом количестве конфигураций обычно используется библиотека merge.
npm install webpack-merge --save-dev
Пример:
const { merge } = require('webpack-merge');
const common = {
entry: './src/index.js'
};
const development = merge(common, {
mode: 'development',
devtool: 'source-map',
output: {
filename: 'dev.js'
}
});
const production = merge(common, {
mode: 'production',
output: {
filename: 'prod.js'
}
});
module.exports = [
development,
production
];
webpack-merge корректно объединяет:
Webpack способен выполнять несколько конфигураций последовательно, однако некоторые плагины могут запускать внутренние процессы параллельно.
При большом количестве сборок важно учитывать:
Крупные проекты нередко разбивают конфигурации на отдельные команды вместо единого массива.
Webpack поддерживает механизм зависимостей между сборками через
dependencies.
module.exports = [
{
name: 'shared',
entry: './src/shared.js',
output: {
filename: 'shared.js'
}
},
{
name: 'app',
dependencies: ['shared'],
entry: './src/app.js',
output: {
filename: 'app.js'
}
}
];
В этом случае:
shared будет выполнена первой;app начнёт сборку только после завершения
shared;Каждая конфигурация может иметь собственный набор плагинов.
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = [
{
name: 'client',
plugins: [
new HtmlWebpackPlugin({
template: './public/index.html'
})
]
},
{
name: 'styles',
plugins: [
new MiniCssExtractPlugin({
filename: 'styles.css'
})
]
}
];
Плагины не разделяются автоматически между конфигурациями.
Каждый объект создаёт собственный экземпляр инфраструктуры Webpack:
Массив конфигураций позволяет гибко управлять source maps.
module.exports = [
{
name: 'debug',
devtool: 'source-map'
},
{
name: 'fast',
devtool: false
}
];
Это удобно при:
Разные конфигурации могут экспортировать различные форматы библиотек.
module.exports = [
{
name: 'commonjs',
output: {
filename: 'library.cjs.js',
libraryTarget: 'commonjs2'
}
},
{
name: 'esm',
experiments: {
outputModule: true
},
output: {
filename: 'library.esm.js',
module: true
}
}
];
Такой подход часто используется при публикации npm-пакетов.
Массив конфигураций подходит для создания нескольких версий приложения:
module.exports = [
{
name: 'desktop',
output: {
filename: 'desktop.js'
}
},
{
name: 'mobile',
output: {
filename: 'mobile.js'
}
}
];
Различия могут касаться:
Webpack позволяет возвращать массив динамически.
module.exports = (env) => {
const isProduction = env.production;
return [
{
name: 'client',
mode: isProduction
? 'production'
: 'development'
},
{
name: 'server',
target: 'node',
mode: isProduction
? 'production'
: 'development'
}
];
};
Запуск:
npx webpack --env production=true
Преимущества:
Ошибка:
output: {
path: path.resolve(__dirname, 'dist')
}
Если несколько сборок пишут одинаковые файлы в одну директорию, возможно:
Лучше использовать отдельные папки:
dist/client
dist/server
dist/legacy
Проблема:
filename: 'bundle.js'
во всех конфигурациях.
Результат:
Правильный вариант:
filename: '[name].[contenthash].js'
или:
filename: 'client.bundle.js'
filename: 'server.bundle.js'
Некоторые плагины потребляют много ресурсов:
TerserPlugin;ImageMinimizerPlugin;ForkTsCheckerWebpackPlugin.Если они присутствуют во всех конфигурациях массива, время сборки может резко увеличиться.
В крупных проектах конфигурации обычно разделяются.
Пример структуры:
webpack/
├── common.js
├── client.js
├── server.js
├── production.js
└── development.js
Главный файл:
const client = require('./webpack/client');
const server = require('./webpack/server');
module.exports = [
client,
server
];
Такой подход:
Webpack CLI поддерживает выбор отдельных конфигураций.
Список имён:
npx webpack --config-name client
Несколько конфигураций:
npx webpack --config-name client --config-name server
Это особенно удобно:
При использовании:
npx webpack --watch
Webpack отслеживает изменения для всех конфигураций массива.
Каждый compiler:
При большом количестве конфигураций это может заметно увеличить нагрузку на файловую систему.
При экспорте массива Webpack создаёт внутренний объект
MultiCompiler.
Схема работы:
Array configs
↓
MultiCompiler
↓
Compiler #1
Compiler #2
Compiler #3
Каждый compiler:
Изоляция конфигураций — важная особенность массива.
Экспорт массива конфигураций применяется в следующих случаях: