Webpack разбивает приложение на набор чанков — отдельных логических
частей графа модулей. Каждый чанк получает собственный идентификатор
(chunk id), который используется:
SplitChunksPlugin;Module Federation;Идентификатор чанка влияет не только на внутреннюю структуру сборки, но и на стабильность кеша браузера. Изменение одного идентификатора способно привести к каскадному изменению множества выходных файлов.
Ранние версии Webpack использовали простую стратегию:
chunk.id = incrementalNumber++;
В результате:
main.js -> id 0
vendors.js -> id 1
profile.js -> id 2
Подобная схема имела несколько серьёзных недостатков.
Добавление нового чанка в середину графа меняет все последующие идентификаторы:
Сборка #1:
0 -> main
1 -> vendors
2 -> profile
Сборка #2:
0 -> main
1 -> analytics
2 -> vendors
3 -> profile
Даже если содержимое vendors не изменилось, его id уже
другой.
Это приводит к:
Webpack постепенно внедрял более сложные схемы генерации идентификаторов.
Алгоритм основан на естественном порядке обнаружения чанков.
Пример:
optimization: {
chunkIds: 'natural'
}
Особенности:
Подходит:
Webpack начал использовать имена чанков:
optimization: {
chunkIds: 'named'
}
Результат:
main
vendors
profile
Преимущества:
Недостатки:
Современный production-подход.
optimization: {
chunkIds: 'deterministic'
}
Webpack генерирует короткий, но стабильный идентификатор на основе содержимого и структуры графа.
Пример:
a1
b4
3f
Главная цель — сохранить id неизменным между сборками, если чанк логически не изменился.
Алгоритм использует несколько источников информации:
Упрощённая схема:
chunk graph
↓
serialization
↓
hash generation
↓
base encoding
↓
final chunk id
Webpack преобразует информацию о чанке в строковое представление.
Например:
src/pages/profile.js
src/components/avatar.js
src/api/user.js
Либо:
runtime=main
group=profile
async=true
После сериализации Webpack создаёт hash:
f83a91dcb7...
Затем используется сокращённая версия.
Например:
f83a
Если структура чанка не изменилась:
src/pages/profile.js
src/components/avatar.js
то id останется прежним даже после:
Это критически важно для:
Одна из главных задач improved chunk ids — минимизация cache invalidation.
Без стабильных идентификаторов:
main.js
vendors.js
profile.js
Добавление нового lazy chunk:
analytics.js
может изменить:
В итоге браузер повторно скачивает почти всё приложение.
Webpack хранит таблицу соответствия:
{
14: '/assets/profile.a1.js',
15: '/assets/vendors.b4.js'
}
Deterministic ids позволяют не менять mapping без необходимости.
Обычно используется:
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js'
}
Но даже при contenthash нестабильные chunk ids способны
менять runtime.
Improved chunk ids уменьшают вероятность изменения:
Webpack применяет несколько внутренних эвристик.
Некоторые алгоритмы отдают приоритет большим чанкам.
Причина:
Webpack пытается сохранить короткие идентификаторы:
a
b
c
d
вместо:
chunk-profile-page
chunk-dashboard-layout
Это уменьшает:
Hash может совпасть:
a1
a1
Webpack автоматически удлиняет идентификатор:
a1
a1f
или:
a1
a1b7
Webpack не использует фиксированную длину hash.
Чем больше проект:
Маленький проект:
a
b
c
Крупный monorepo:
a91
f3d
bb7
Во время выполнения Webpack runtime использует chunk ids для загрузки файлов.
Пример:
__webpack_require__.e(14)
Runtime ищет:
14 -> profile.a1.js
После чего создаётся:
<script src="/assets/profile.a1.js">
Классический runtime:
webpackChunk.push([
[14],
modules
]);
Число 14 — идентификатор чанка.
Если ids нестабильны:
Современный Webpack использует полноценный chunk graph.
Ранее использовалась линейная модель:
entry -> modules
Она плохо работала:
Теперь используется сложная графовая модель:
Entry
├── vendors
├── dashboard
│ └── charts
└── profile
Алгоритм генерации id анализирует:
SplitChunksPlugin напрямую влияет на генерацию chunk
ids.
Пример:
optimization: {
splitChunks: {
chunks: 'all'
}
}
Webpack создаёт:
vendors
common
shared
Для них особенно важна стабильность.
Webpack пытается сохранить идентификатор shared chunk, если:
В микрофронтендах стабильность id становится критически важной.
Remote container может загружать чанки динамически:
import('shop/ProductPage');
При изменении chunk ids:
Deterministic ids позволяют:
Tree shaking изменяет состав модулей внутри чанков.
Пример:
import { a } from './utils';
Unused exports удаляются:
before:
a b c d
after:
a
Это способно изменить hash чанка.
Webpack старается минимизировать влияние:
Каждый import() создаёт потенциальный async chunk.
import('./dashboard');
Webpack генерирует:
dashboard.chunk.js
или:
391.js
Improved ids уменьшают вероятность того, что:
Можно комбинировать deterministic ids и naming.
import(
/* webpackChunkName: "dashboard" */
'./dashboard'
);
Webpack использует:
dashboard.js
или:
dashboard.a91.js
Часто применяется:
output: {
chunkFilename: '[name].[contenthash].js'
}
совместно с:
optimization: {
chunkIds: 'deterministic'
}
Это обеспечивает:
Runtime содержит таблицы соответствия ids и файлов.
Большие ids увеличивают:
Webpack балансирует между:
| Алгоритм | Стабильность | Размер runtime | Production |
|---|---|---|---|
| natural | низкая | минимальный | плохо |
| named | средняя | высокий | ограниченно |
| deterministic | высокая | низкий | оптимально |
module.exports = {
mode: 'production',
optimization: {
chunkIds: 'deterministic',
moduleIds: 'deterministic',
runtimeChunk: 'single',
splitChunks: {
chunks: 'all'
}
},
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].js'
}
};
optimization: {
runtimeChunk: 'single'
}
Позволяет изолировать:
При изменении одного чанка остальные файлы чаще сохраняют прежний
contenthash.
Webpack предоставляет статистику:
webpack --json > stats.json
Можно анализировать:
Частые симптомы:
contenthash;optimization: {
chunkIds: 'natural'
}
Приводит к:
Без изоляции runtime:
runtimeChunk: false
изменение одного чанка способно менять bootstrap.
Слишком агрессивный splitting:
splitChunks: {
minSize: 0
}
создаёт множество мелких чанков.
Это повышает:
Webpack 5 существенно переработал систему идентификаторов.
Появился отдельный API:
compilation.chunkGraph
Он хранит:
Webpack разделяет:
Это уменьшает каскадные изменения.
Внутренний плагин:
DeterministicChunkIdsPlugin
реализует:
Сложные алгоритмы увеличивают:
Но выигрыш в caching обычно значительно важнее.
Особенно:
В watch mode Webpack старается:
Это уменьшает:
Hot Module Replacement использует:
Стабильные идентификаторы уменьшают:
Webpack 5:
Особенно заметно это в проектах с: