SplitChunksPlugin
Изначально, модули (и импортированные внутри них модули) были связаны родительско-детскими отношениями во внутренней графе webpack. CommonsChunkPlugin использовалось для предотвращения дублирования зависимостей между ними, но дальнейшая оптимизация была невозможна.
Начиная с webpack v4, CommonsChunkPlugin было удалено в пользу optimization.splitChunks.
Значения по умолчанию
Из коробки SplitChunksPlugin должно хорошо работать для большинства пользователей.
По умолчанию, оно влияет только на загружаемые по запросу чанки, потому что изменение начальных чанков повлияет на тег script, которые должен включать HTML-файл для запуска проекта.
Webpack автоматически разделит чанки на основе следующих условий:
- Новый чанк может быть общим ИЛИ модули взяты из папки
node_modules - Новый чанк будет больше 20 КБ (до минимизации + gzip)
- Максимальное количество параллельных запросов при загрузке чанков по требованию будет меньше или равно 30
- Максимальное количество параллельных запросов при начальной загрузке страницы будет меньше или равно 30
При попытке выполнить последние два условия, отдаётся предпочтение большим чанкам.
Конфигурация
Webpack предоставляет набор опций для разработчиков, которые хотят получить больший контроль над этой функциональностью.
optimization.splitChunks
Этот объект конфигурации представляет поведение по умолчанию SplitChunksPlugin.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
chunks: 'async',
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
},
},
}; splitChunks.automaticNameDelimiter
string = '~'
По умолчанию webpack будет генерировать имена, используя исходник и имя чанка (например, vendors~main.js). Эта опция позволяет указать разделитель, используемый для сгенерированных имён.
splitChunks.chunks
string = 'async' function (chunk) RegExp
Это указывает, какие чанки будут выбраны для оптимизации. Когда передаётся строка, допустимые значения — all, async, и initial. Передача all может быть особенно мощной, потому что это означает, что чанки могут быть общими даже между асинхронными и не асинхронными чанками.
Обратите внимание, что это также применяется к группе кэширования по умолчанию (splitChunks.fallbackCacheGroup.chunks).
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';
},
},
},
}; Если вы используете webpack версии 5.86.0 или новее, вы также можете передать регулярное выражение:
module.exports = {
//...
optimization: {
splitChunks: {
chunks: /foo/,
},
},
}; splitChunks.maxAsyncRequests
number = 30
Максимальное количество параллельных запросов при загрузке по требованию.
splitChunks.maxInitialRequests
number = 30
Максимальное количество параллельных запросов при входной точке.
splitChunks.defaultSizeTypes
[string] = ['javascript', 'unknown']
Задает типы размеров, которые используются при использовании чисел для размеров.
splitChunks.minChunks
number = 1
Минимальное количество раз, когда модуль должен быть общим для чанков перед разделением.
splitChunks.hidePathInfo
boolean
Предотвращает раскрытие информации о пути при создании имён для частей, разделенных по maxSize.
splitChunks.minSize
number = 20000 { [index: string]: number }
Минимальный размер в байтах для генерации чанка.
splitChunks.minSizeReduction
number { [index: string]: number }
Минимальное уменьшение размера основного чанка (пакета) в байтах, необходимое для генерации чанка. Это означает, что если разделение в чанк не уменьшает размер основного чанка (пакета) на заданную величину байтов, он не будет разделен, даже если он удовлетворяет splitChunks.minSize значению.
splitChunks.enforceSizeThreshold
splitChunks.cacheGroups.{cacheGroup}.enforceSizeThreshold
number = 50000
Пороговое значение размера, при котором разделение применяется, и другие ограничения (minRemainingSize, maxAsyncRequests, maxInitialRequests) игнорируются.
splitChunks.minRemainingSize
splitChunks.cacheGroups.{cacheGroup}.minRemainingSize
number = 0
Опция splitChunks.minRemainingSize была введена в webpack 5, чтобы избежать модулей нулевого размера, гарантируя, что минимальный размер чанка, оставшегося после разделения, превышает предел. По умолчанию равно 0 в режиме 'development' mode. В других случаях splitChunks.minRemainingSize по умолчанию соответствует значению splitChunks.minSize, поэтому его не нужно указывать вручную, за исключением редких случаев, когда требуется глубокий контроль.
splitChunks.layer
splitChunks.cacheGroups.{cacheGroup}.layer
RegExp string function
Назначение модулей в группу кэша по слою модуля.
splitChunks.maxSize
number = 0
Использование 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 и кэшированием длительного срока. Она увеличивает количество запросов для лучшего кэширования. Она также может быть использована для уменьшения размера файла для более быстрого перестроения.
splitChunks.maxAsyncSize
number
Как и maxSize, maxAsyncSize может применяться глобально (splitChunks.maxAsyncSize), к группам кэша (splitChunks.cacheGroups.{cacheGroup}.maxAsyncSize) или к группе кэша по умолчанию (splitChunks.fallbackCacheGroup.maxAsyncSize).
Разница между maxAsyncSize и maxSize заключается в том, что maxAsyncSize будет влиять только на чанки, загружаемые по требованию.
splitChunks.maxInitialSize
number
Как и maxSize, maxInitialSize может применяться глобально (splitChunks.maxInitialSize), к группам кэша (splitChunks.cacheGroups.{cacheGroup}.maxInitialSize) или к группе кэша по умолчанию (splitChunks.fallbackCacheGroup.maxInitialSize).
Разница между maxInitialSize и maxSize заключается в том, что maxInitialSize будет влиять только на чанки начальной загрузки.
splitChunks.name
boolean = false function (module, chunks, cacheGroupKey) => string string
Также доступно для каждой группы кэша: splitChunks.cacheGroups.{cacheGroup}.name.
Имя разделяемого чанка. Передача false сохранит то же имя чанков, чтобы излишне не изменять имена. Это рекомендуемое значение для производственных сборок.
Передача строки или функции позволяет использовать пользовательское имя. Указание строки или функции, которая всегда возвращает одну и ту же строку, объединит все общие модули и vendor модули в один чанк. Это может привести к большим начальным загрузкам и замедлению загрузки страницы.
Если вы выберете указание функции, вы можете найти свойство chunk.name (где chunk — элемент массива chunks) особенно полезным при выборе имени для вашего чанка.
Если splitChunks.name совпадает с именем точки входа, чанк точки входа и группа кэша будут объединены в один чанк.
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.usedExports
splitChunks.cacheGroups{cacheGroup}.usedExports
boolean = true
Определение используемых модулей для приведения имен экспорта, исключения неиспользуемых экспортов и генерации более эффективного кода. Если оно true: анализирует используемые экспорты для каждого выполнения, если оно "global": анализирует экспорты глобально для всех выполнений вместе.
splitChunks.cacheGroups
Группы кэша могут наследовать и/или переопределять любые параметры из splitChunks.*; но test, priority и reuseExistingChunk могут быть сконфигурированы только на уровне группы кэша. Чтобы отключить любые группы кэша по умолчанию, установите их в false.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
default: false,
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.priority
number = -20
Модуль может принадлежать нескольким группам кэша. Оптимизация отдаст предпочтение группе кэша с более высоким priority. У групп по умолчанию отрицательный приоритет, чтобы позволить пользовательским группам иметь более высокий приоритет (значение по умолчанию для пользовательских групп 0).
splitChunks.cacheGroups.{cacheGroup}.reuseExistingChunk
boolean = true
Если текущий фрагмент содержит модули, уже разделенные из основного пакета, он будет повторно использован вместо создания нового. Это может повлиять на имя результирующего файла фрагмента.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
reuseExistingChunk: true,
},
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.type
function RegExp string
Позволяет назначать модули в группу кэша по типу модуля.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
json: {
type: 'json',
},
},
},
},
}; splitChunks.cacheGroups.test
splitChunks.cacheGroups.{cacheGroup}.test
function (module, { chunkGraph, moduleGraph }) => boolean RegExp string
Управляет выбором модулей этой группой кэша. Пропуская этот параметр, выбираются все модули. Он может соответствовать абсолютному пути к ресурсу модуля или именам фрагментов. При соответствии имени фрагмента, выбираются все модули в фрагменте.
Предоставление функции для{cacheGroup}.test:
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
svgGroup: {
test(module) {
// `module.resource` contains the absolute path of the file on disk.
// Note the usage of `path.sep` instead of / or \, for cross-platform compatibility.
const path = require('path');
return (
module.resource &&
module.resource.endsWith('.svg') &&
module.resource.includes(`${path.sep}cacheable_svgs${path.sep}`)
);
},
},
byModuleTypeGroup: {
test(module) {
return module.type === 'javascript/auto';
},
},
},
},
},
}; Для просмотра доступной информации в объектах module и chunks, можно поместить инструкцию debugger; в коллбэк. Затем запустите сборку webpack в отладочном режиме для проверки параметров в Chromium DevTools.
Предоставление RegExp к {cacheGroup}.test:
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
// Note the usage of `[\\/]` as a path separator for cross-platform compatibility.
test: /[\\/]node_modules[\\/]|vendor[\\/]analytics_provider|vendor[\\/]other_lib/,
},
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.filename
string function (pathData, assetInfo) => string
Позволяет переопределить имя файла только тогда, когда это начальный фрагмент. Все доступные плейсхолдеры в output.filename также доступны здесь.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
filename: '[name].bundle.js',
},
},
},
},
}; И как функция:
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
filename: (pathData) => {
// Use pathData object for generating filename string based on your requirements
return `${pathData.chunk.name}-bundle.js`;
},
},
},
},
},
}; Можно создать структуру папок, предоставив префикс пути к имени файла: 'js/vendor/bundle.js'.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
filename: 'js/[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: {
defaultVendors: {
enforce: true,
},
},
},
},
};
splitChunks.cacheGroups.{cacheGroup}.idHint
string
Устанавливает подсказку для идентификатора фрагмента. Она будет добавлена к имени файла фрагмента.
webpack.config.js
module.exports = {
//...
optimization: {
splitChunks: {
cacheGroups: {
defaultVendors: {
idHint: 'vendors',
},
},
},
},
}; Примеры
По умолчанию: Пример 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',
},
},
},
},
};
© JS Foundation and other contributors
Licensed under the Creative Commons Attribution License 4.0.
https://webpack.js.org/plugins/split-chunks-plugin