15. Кэширование и хэши
Правильная стратегия кэширования — одна из самых важных оптимизаций для production-приложений. Цель: браузер должен кэшировать файлы как можно дольше, но мгновенно получать обновления при изменении кода.
Проблема без кэширования
Заголовок раздела «Проблема без кэширования»Без правильного кэширования каждое обновление приложения заставляет пользователей перезагружать все файлы, включая неизменённые (React, lodash и т.д.):
Без кэширования:bundle.js → Загрузка 580 KB при каждом обновлении
С правильным кэшированием:main.a1b2c3.js (собственный код) → Загрузка 85 KB при обновленииvendor.d4e5f6.js (React + libs) → Из кеша (не изменился)Content Hash — основа кэширования
Заголовок раздела «Content Hash — основа кэширования»output: { filename: '[name].[contenthash:8].js', // contenthash меняется только если содержимое файла изменилось // Не меняется если изменился другой файл!}Разница между хешами:
| Тип хеша | Изменяется при | Использование |
|---|---|---|
[hash] | Любом изменении в сборке | Устарел, не рекомендуется |
[chunkhash] | Изменении конкретного чанка | JS бандлы (устаревает) |
[contenthash] | Изменении содержимого файла | Рекомендуется для всего |
runtimeChunk — изоляция webpack runtime
Заголовок раздела «runtimeChunk — изоляция webpack runtime»optimization: { runtimeChunk: 'single', // Webpack runtime (маппинг чанков) в отдельный файл // Без этого: изменение одного файла → инвалидируются хеши всех бандлов!}Без runtimeChunk:
main.abc123.js ← содержит runtime + кодvendors.def456.js
# При изменении App.tsx:main.xyz789.js ← хеш изменился ✓vendors.mno012.js ← хеш тоже изменился! ✗ (runtime обновился)С runtimeChunk: 'single':
runtime.qqq111.js ← только runtime (маленький, часто меняется)main.abc123.js ← только код (меняется при изменении App.tsx)vendors.def456.js ← библиотеки (почти никогда не меняются) ✓cacheGroups — группировка модулей
Заголовок раздела «cacheGroups — группировка модулей»optimization: { splitChunks: { cacheGroups: { // React и его друзья — меняются редко (при обновлении версий) react: { test: /[\\/]node_modules[\\/](react|react-dom|react-router)[\\/]/, name: 'react-vendor', chunks: 'all', priority: 30, }, // Все остальные node_modules vendors: { test: /[\\/]node_modules[\\/]/, name: 'vendors', chunks: 'all', priority: 20, }, // Общий код приложения (утилиты, hooks) common: { name: 'common', minChunks: 2, priority: 10, reuseExistingChunk: true, }, }, },}Persistent Caching — ускорение сборки
Заголовок раздела «Persistent Caching — ускорение сборки»cache: { type: 'filesystem', // Кешировать на диск между сборками cacheDirectory: path.resolve(__dirname, '.webpack-cache'),
// Инвалидировать кеш при изменении конфига buildDependencies: { config: [__filename], },
// Версия кеша (менять при breaking changes) version: '1.0.0',}
// Результат:// Первая сборка: 45s// Повторная сборка: 2-3s! 🚀Настройка HTTP заголовков сервера
Заголовок раздела «Настройка HTTP заголовков сервера»# Nginx — длинный кеш для файлов с хешем в имениlocation ~* \.[0-9a-f]{8}\.(js|css)$ { expires 1y; add_header Cache-Control "public, immutable";}
# Короткий кеш для index.html (не имеет хеша)location = /index.html { expires 0; add_header Cache-Control "no-cache";}Полный конфиг кэширования
Заголовок раздела «Полный конфиг кэширования»module.exports = { mode: 'production', output: { filename: '[name].[contenthash:8].js', chunkFilename: '[id].[contenthash:8].js', assetModuleFilename: 'assets/[hash:8][ext]', clean: true, }, optimization: { runtimeChunk: 'single', moduleIds: 'deterministic', // Стабильные ID модулей chunkIds: 'deterministic', splitChunks: { chunks: 'all', cacheGroups: { react: { test: /react/, name: 'react-vendor', priority: 30, chunks: 'all' }, vendors: { test: /node_modules/, name: 'vendors', priority: 20, chunks: 'all' }, common: { name: 'common', minChunks: 2, priority: 10 }, }, }, }, cache: { type: 'filesystem', buildDependencies: { config: [__filename] }, },};Ниже — интерактивный симулятор кэширования. Изменяйте файлы и смотрите как меняются хеши бандлов.