SplitChunksPlugin
Изначально, чанки (и модули, импортированные внутри них) были связаны родительско-дочерней зависимостью в внутренней графе webpack. CommonsChunkPlugin использовалась для предотвращения дублирования зависимостей между ними, но дальнейшая оптимизация была невозможна.
С версии webpack v4 CommonsChunkPlugin была удалена в пользу optimization.splitChunks.
Значения по умолчанию
В базовом варианте SplitChunksPlugin должны хорошо работать для большинства пользователей.
По умолчанию, он затрагивает только чанки по запросу, потому что изменение начальных чанков повлияет на тег script, которые HTML-файл должен включать для запуска проекта.
webpack автоматически разделит чанки на основе следующих условий:
- Новый чанк может быть общим ИЛИ модули взяты из папки
node_modules - Новый чанк будет больше, чем 30 Кб (до сжатия min+gz)
- Максимальное количество параллельных запросов при загрузке чанков по запросу будет меньше или равно 5
- Максимальное количество параллельных запросов при начальной загрузке страницы будет меньше или равно 3
При попытке выполнить последние два условия, предпочтение отдаётся более крупным чанкам.
Настройка
webpack предоставляет набор опций для разработчиков, которые хотят получить больший контроль над этой функциональностью.
Настройка по умолчанию была выбрана для наилучшего соответствия лучшим практикам производительности веб-приложений, но оптимальная стратегия для вашего проекта может отличаться. Если вы изменяете конфигурацию, вы должны измерить влияние ваших изменений, чтобы убедиться, что есть реальная польза.
optimization.splitChunks
Этот объект конфигурации представляет собой поведение по умолчанию SplitChunksPlugin.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 30000,
maxSize: 0,
minChunks: 1,
maxAsyncRequests: 5,
maxInitialRequests: 3,
automaticNameDelimiter: '~',
automaticNameMaxLength: 30,
name: true,
cacheGroups: {
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
}
};
splitChunks.automaticNameDelimiter
string
По умолчанию webpack будет генерировать имена, используя исходное имя и имя чанка (например, vendors~main.js). Эта опция позволяет указать разделитель для сгенерированных имён.
splitChunks.automaticNameMaxLength
number = 109
Позволяет установить максимальное количество символов для имён чанков, сгенерированных SplitChunksPlugin.
splitChunks.chunks
function (chunk) string
Это указывает, какие чанки будут выбраны для оптимизации. При указании строки, допустимыми значениями являются all, async, и initial. Указание all может быть особенно мощным, так как это означает, что чанки могут быть объединены даже между асинхронными и неасинхронными чанками.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
// include all types of chunks
chunks: 'all'
}
}
}; В качестве альтернативы можно указать функцию для большего контроля. Значение возврата будет указывать, нужно ли включать каждый чанк.
module.exports = {
//...
optimization: {
splitChunks: {
chunks (chunk) {
// exclude `my-excluded-chunk`
return chunk.name !== 'my-excluded-chunk';
}
}
}
}; Вы можете комбинировать эту настройку с HtmlWebpackPlugin. Он вставит все сгенерированные чанки вендоров для вас.
splitChunks.maxAsyncRequests
number
Максимальное количество параллельных запросов при загрузке по запросу.
splitChunks.maxInitialRequests
number
Максимальное количество параллельных запросов на точке входа.
splitChunks.minChunks
number
Минимальное количество чанков, которые должны совместно использовать модуль перед разделением.
splitChunks.minSize
number
Минимальный размер в байтах для создания чанка.
splitChunks.maxSize
number
Использование maxSize (либо глобально optimization.splitChunks.maxSize по группам кэша optimization.splitChunks.cacheGroups[x].maxSize или для группы кэша по умолчанию optimization.splitChunks.fallbackCacheGroup.maxSize ) сообщает webpack, что необходимо попытаться разбить чанки, размер которых больше maxSize байт, на более мелкие части. Части будут иметь размер не менее minSize (рядом с maxSize). Алгоритм детерминированный, и изменения в модулях будут иметь только локальное влияние. Поэтому он может быть использован при использовании долговременного кэширования и не требует записей. maxSize является лишь подсказкой и может быть нарушена, если модули имеют размер больше maxSize или разделение нарушит minSize.
Если чанк уже имеет имя, каждая часть получит новое имя, полученное из этого имени. В зависимости от значения optimization.splitChunks.hidePathInfo будет добавлена метка, полученная от первого имени модуля или хэш этого имени.
Опция maxSize предназначена для использования с HTTP/2 и долговременным кэшированием. Она увеличивает количество запросов для лучшего кэширования. Она также может использоваться для уменьшения размера файла для более быстрого перестроения.
maxSizeимеет более высокий приоритет, чемmaxInitialRequest/maxAsyncRequests. Действительный приоритет -maxInitialRequest/maxAsyncRequests < maxSize < minSize.
splitChunks.name
boolean = true function (module, chunks, cacheGroupKey) => string string
Также доступно для каждой группы кэша: splitChunks.cacheGroups.{cacheGroup}.name.
Имя разделяемого чанка. Указание true автоматически сгенерирует имя на основе чанков и ключа группы кэша.
Указание строки или функции позволяет использовать пользовательское имя. Указание строки или функции, которая всегда возвращает ту же строку, объединит все общие модули и вендоры в один чанк. Это может привести к более большим начальным загрузкам и замедлить загрузку страницы.
Если вы выберите указание функции, вам могут оказаться полезными свойства chunk.name и chunk.hash (где chunk является элементом массива chunks) для выбора имени вашего чанка.
Если splitChunks.name соответствует имени точки входа точки входа, точка входа будет удалена.
Рекомендуется установить
splitChunks.nameнаfalseдля билдов в продакшене, чтобы имена не менялись ненужно.
main.js
import _ from 'lodash'; console.log(_.join(['Hello', 'webpack'], ' '));
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
// cacheGroupKey here is `commons` as the key of the cacheGroup
name(module, chunks, cacheGroupKey) {
const moduleFileName = module.identifier().split('/').reduceRight(item => item);
const allChunksNames = chunks.map((item) => item.name).join('~');
return `${cacheGroupKey}-${allChunksNames}-${moduleFileName}`;
},
chunks: 'all'
}
}
}
}
}; Запуск webpack с указанной splitChunks конфигурацией также выведет чанк группы common со следующим именем: commons-main-lodash.js.e7519d2bb8777058fa27.js (хэш указан как пример реального вывода).
При присвоении одинаковых имён разным разделяемым чанкам, все модули вендоров размещаются в одном общем чанке, хотя это не рекомендуется, так как это может привести к загрузке большего количества кода.
splitChunks.cacheGroups
Группы кэша могут наследовать и/или переопределять любые параметры из splitChunks.*; но test, priority и reuseExistingChunk могут быть настроены только на уровне группы кэша. Чтобы отключить любые группы кэша по умолчанию, установите их в false.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
default: false
}
}
}
};
splitChunks.cacheGroups.{cacheGroup}.priority
number
Модуль может принадлежать нескольким группам кэша. Оптимизация будет отдавать предпочтение группе кэша с более высоким priority. Группы по умолчанию имеют отрицательный приоритет, чтобы пользовательские группы могли иметь более высокий приоритет (значение по умолчанию - 0 для пользовательских групп).
splitChunks.cacheGroups.{cacheGroup}.reuseExistingChunk
boolean
Если текущий чанк содержит модули, уже разделенные из основного пакета, он будет повторно использован вместо генерации нового. Это может повлиять на результирующее имя чанка.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
reuseExistingChunk: true
}
}
}
}
};
splitChunks.cacheGroups.{cacheGroup}.test
function (module, chunk) => boolean RegExp string
Управляет тем, какие модули выбираются этой группой кэша. Пропуск этого значения выбирает все модули. Он может сопоставлять абсолютный путь к ресурсу модуля или имена чанков. При совпадении имени чанка выбираются все модули в чанке.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
test(module, chunks) {
//...
return module.type === 'javascript/auto';
}
}
}
}
}
};
splitChunks.cacheGroups.{cacheGroup}.filename
string
Позволяет переопределить имя файла только при условии, что это начальный чанк. Все плейсхолдеры, доступные в output.filename, также доступны здесь.
Эта опция также может быть установлена глобально в
splitChunks.filename, но это не рекомендуется и, скорее всего, приведёт к ошибке, еслиsplitChunks.chunksне установлено в'initial'. Избегайте её глобальной установки.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
filename: '[name].bundle.js'
}
}
}
}
};
splitChunks.cacheGroups.{cacheGroup}.enforce
boolean = false
Сообщает webpack игнорировать splitChunks.minSize, splitChunks.minChunks, splitChunks.maxAsyncRequests и splitChunks.maxInitialRequests опции и всегда создавать чанки для этой группы кэша.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendors: {
enforce: true
}
}
}
}
}; Примеры
Примеры значений по умолчанию: пример 1
// index.js
import('./a'); // dynamic import // a.js import 'react'; //...
Результат: Был создан отдельный чанк, содержащий react. При вызове импорта этот чанк загружается параллельно с исходным чанком, содержащим ./a.
Почему:
- Условие 1: Чанк содержит модули из
node_modules - Условие 2:
reactбольше, чем 30 Кб - Условие 3: Количество параллельных запросов при вызове импорта равно 2
- Условие 4: Не влияет на запросы при начальной загрузке
Почему так? react скорее всего будет изменяться реже, чем ваш код приложения. Переместив его в отдельный чанк, этот чанк может быть кэширован отдельно от кода вашего приложения (при условии, что вы используете chunkhash, записи, Cache-Control или другой подход к долговременному кэшированию).
Примеры значений по умолчанию: пример 2
// entry.js
// dynamic imports
import('./a');
import('./b'); // a.js import './helpers'; // helpers is 40kb in size //...
// b.js import './helpers'; import './more-helpers'; // more-helpers is also 40kb in size //...
Результат: Был создан отдельный чанк, содержащий ./helpers и все его зависимости. При вызове импорта этот чанк загружается параллельно с исходными чанками.
Почему:
- Условие 1: Чанк общий для обоих вызовов импорта
- Условие 2:
helpersбольше, чем 30 Кб - Условие 3: Количество параллельных запросов при вызове импорта равно 2
- Условие 4: Не влияет на запросы при начальной загрузке
Размещение содержимого helpers в каждом фрагменте приведёт к тому, что его код будет загружен дважды. Использование отдельного фрагмента позволит загрузить его только один раз. Мы платим за дополнительный запрос, что может рассматриваться как компромисс. Поэтому минимальный размер составляет 30 КБ.
Примеры разделения фрагментов: пример 1
Создайте фрагмент commons, включающий весь код, общий для точек входа.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
name: 'commons',
chunks: 'initial',
minChunks: 2
}
}
}
}
}; Эта конфигурация может увеличить начальные бандлы, поэтому рекомендуется использовать динамические импорты, когда модуль не нужен немедленно.
Примеры разделения фрагментов: пример 2
Создайте фрагмент vendors, включающий весь код из node_modules во всем приложении.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
}; Это может привести к большому фрагменту, содержащему все внешние пакеты. Рекомендуется включать только свои основные фреймворки и утилиты, а остальную часть зависимостей загружать динамически.
Примеры разделения фрагментов: пример 3
Создайте фрагмент custom vendor , содержащий определённые node_modules пакеты, соответствующие RegExp.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/](react|react-dom)[\\/]/,
name: 'vendor',
chunks: 'all',
}
}
}
}
}; Это приведёт к разделению
reactиreact-domна отдельный фрагмент. Если вы не уверены, какие пакеты включены в фрагмент, вы можете обратиться к разделу «Анализ бандлов» в руководстве Анализ бандлов для получения подробностей.
© JS Foundation and other contributors
Licensed under the Creative Commons Attribution License 4.0.
https://v4.webpack.js.org/plugins/split-chunks-plugin