Постоянное кэширование и contenthash

Постобработка в Webpack — совокупность этапов, выполняемых после основной трансформации исходного кода. На этой стадии выполняются оптимизация, минификация, очистка артефактов, анализ бандла, генерация карт исходников, копирование ресурсов, компрессия файлов и другие действия, подготавливающие сборку к production-использованию.

Webpack разделяет процесс сборки на несколько фаз:

  1. Чтение графа зависимостей
  2. Трансформация модулей через loaders
  3. Построение чанков
  4. Генерация ассетов
  5. Постобработка и оптимизация
  6. Эмиссия файлов

Именно на поздних стадиях активно работают плагины оптимизации.


Роль постобработки

Без постобработки итоговый бандл обычно:

  • содержит неиспользуемый код;
  • имеет избыточные комментарии;
  • включает дублирующиеся зависимости;
  • обладает большим размером;
  • плохо кэшируется;
  • не оптимизирован для браузера.

Постобработка решает следующие задачи:

Задача Результат
Минификация уменьшение размера JS/CSS
Tree Shaking удаление мёртвого кода
Code Splitting разделение бандла
Хеширование эффективное кэширование
Сжатие gzip/brotli
Оптимизация CSS удаление дубликатов
Анализ сборки поиск проблем
Очистка output удаление старых файлов

Архитектура оптимизаций Webpack

Основная часть постобработки настраивается через секцию optimization.

module.exports = {
  optimization: {
    minimize: true
  }
};

Webpack запускает оптимизаторы после формирования чанков и перед записью файлов на диск.


optimization.minimize

Параметр optimization.minimize включает или отключает минимизацию ресурсов.

module.exports = {
  optimization: {
    minimize: true
  }
};

По умолчанию:

  • в production: true
  • в development: false

При отключении:

module.exports = {
  optimization: {
    minimize: false
  }
};

Webpack перестаёт:

  • сокращать переменные;
  • удалять комментарии;
  • выполнять dead-code elimination;
  • уплотнять выражения.

Размер сборки существенно увеличивается.


TerserPlugin

Основной минификатор JavaScript в Webpack 5 — TerserPlugin.

npm install terser-webpack-plugin --save-dev

Подключение

const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  optimization: {
    minimizer: [
      new TerserPlugin()
    ]
  }
};

Что делает Terser

Terser выполняет:

  • удаление пробелов;
  • удаление комментариев;
  • сокращение имён;
  • упрощение выражений;
  • inline-оптимизации;
  • dead-code elimination;
  • constant folding;
  • tree shaking support.

compress

Опция compress отвечает за логические оптимизации.

new TerserPlugin({
  terserOptions: {
    compress: true
  }
});

Удаление console.log

new TerserPlugin({
  terserOptions: {
    compress: {
      drop_console: true
    }
  }
});

Удаляются:

  • console.log
  • console.info
  • console.debug

Удаление debugger

compress: {
  drop_debugger: true
}

mangle

mangle сокращает имена переменных и функций.

Исходный код:

function calculateTotalPrice(products) {
  return products.reduce((sum, item) => {
    return sum + item.price;
  }, 0);
}

После минификации:

function n(n){return n.reduce(((n,r)=>n+r.price),0)}

keep_classnames и keep_fnames

Иногда имена классов и функций нужны для:

  • reflection;
  • dependency injection;
  • логирования;
  • сериализации.
new TerserPlugin({
  terserOptions: {
    keep_classnames: true,
    keep_fnames: true
  }
});

extractComments

Управление извлечением комментариев лицензий.

new TerserPlugin({
  extractComments: false
});

Без этой настройки Webpack может создавать отдельные .LICENSE.txt.


parallel

Параллельная минификация.

new TerserPlugin({
  parallel: true
});

Webpack использует worker threads.

На больших проектах это существенно ускоряет сборку.


CSS Minimizer Plugin

Для CSS используется отдельный минимизатор.

npm install css-minimizer-webpack-plugin --save-dev

Подключение

const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');

module.exports = {
  optimization: {
    minimizer: [
      new CssMinimizerPlugin()
    ]
  }
};

Что оптимизируется в CSS

Минификатор CSS:

  • удаляет пробелы;
  • объединяет правила;
  • сокращает цвета;
  • удаляет дубликаты;
  • оптимизирует shorthand-свойства.

Пример

До:

.button {
  margin-top: 10px;
  margin-right: 10px;
  margin-bottom: 10px;
  margin-left: 10px;
}

После:

.button{margin:10px}

Image Minimizer

Оптимизация изображений уменьшает размер ассетов.

npm install image-minimizer-webpack-plugin imagemin --save-dev

Конфигурация

const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');

module.exports = {
  optimization: {
    minimizer: [
      new ImageMinimizerPlugin({
        minimizer: {
          implementation:
            ImageMinimizerPlugin.imageminGenerate,
          options: {
            plugins: [
              ['mozjpeg', { quality: 70 }],
              ['pngquant', { quality: [0.6, 0.8] }]
            ]
          }
        }
      })
    ]
  }
};

CompressionPlugin

Создание gzip/brotli-файлов.

npm install compression-webpack-plugin --save-dev

Подключение

const CompressionPlugin = require(
  'compression-webpack-plugin'
);

module.exports = {
  plugins: [
    new CompressionPlugin()
  ]
};

Генерация gzip

Webpack создаёт:

app.js
app.js.gz

Сервер может автоматически отдавать сжатую версию.


BrotliCompressionPlugin

Brotli обеспечивает лучшее сжатие, чем gzip.

npm install brotli-webpack-plugin --save-dev

Пример

const BrotliPlugin = require(
  'brotli-webpack-plugin'
);

module.exports = {
  plugins: [
    new BrotliPlugin()
  ]
};

CleanWebpackPlugin

Очистка output-директории перед сборкой.

npm install clean-webpack-plugin --save-dev

Использование

const { CleanWebpackPlugin } = require(
  'clean-webpack-plugin'
);

module.exports = {
  plugins: [
    new CleanWebpackPlugin()
  ]
};

Проблема накопления старых файлов

Без очистки:

dist/
  app.a1.js
  app.b2.js
  app.c3.js

После множества сборок директория засоряется.


CopyWebpackPlugin

Копирование статических ресурсов.

npm install copy-webpack-plugin --save-dev

Конфигурация

const CopyPlugin = require(
  'copy-webpack-plugin'
);

module.exports = {
  plugins: [
    new CopyPlugin({
      patterns: [
        {
          from: 'public',
          to: 'assets'
        }
      ]
    })
  ]
};

BannerPlugin

Добавление баннера в начало файлов.

const webpack = require('webpack');

module.exports = {
  plugins: [
    new webpack.BannerPlugin({
      banner: 'Copyright 2026'
    })
  ]
};

Source Maps

Карты исходников связывают минифицированный код с оригинальным.


Production source maps

module.exports = {
  devtool: 'source-map'
};

Webpack создаёт:

bundle.js
bundle.js.map

Hidden Source Maps

devtool: 'hidden-source-map'

Файл карты создаётся, но не подключается автоматически.

Используется для:

  • Sentry;
  • Rollbar;
  • серверного логирования ошибок.

nosources-source-map

devtool: 'nosources-source-map'

Карта содержит стек вызовов без исходного кода.


Eval Source Maps

devtool: 'eval'

Очень быстрый режим для development.

Не подходит для production.


Bundle Analyzer

Анализ размеров бандла.

npm install webpack-bundle-analyzer --save-dev

Использование

const {
  BundleAnalyzerPlugin
} = require('webpack-bundle-analyzer');

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};

Что показывает анализатор

Bundle Analyzer отображает:

  • размер чанков;
  • размер модулей;
  • дубликаты;
  • vendor-зависимости;
  • tree shaking;
  • gzip size.

Поиск тяжёлых библиотек

Часто обнаруживаются:

  • огромные UI-фреймворки;
  • неиспользуемые локали;
  • polyfills;
  • duplicated packages.

Tree Shaking

Tree shaking — удаление неиспользуемого кода.

Webpack использует:

  • ES Modules;
  • статический анализ import/export.

Пример

export function a() {}
export function b() {}
export function c() {}

Импорт:

import { a } from './utils';

Webpack удалит b и c.


sideEffects

Поле sideEffects помогает оптимизатору.

{
  "sideEffects": false
}

Webpack получает возможность безопасно удалять модули.


Проблема side effects

Некоторые модули имеют побочные эффекты:

import './styles.css';

Удаление такого импорта ломает приложение.


Массив исключений

{
  "sideEffects": [
    "*.css"
  ]
}

SplitChunksPlugin

Разделение кода на чанки.

module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all'
    }
  }
};

Зачем нужен splitChunks

Преимущества:

  • уменьшение initial bundle;
  • повторное использование vendor-кода;
  • better caching;
  • lazy loading.

Runtime Chunk

Выделение runtime-кода.

optimization: {
  runtimeChunk: 'single'
}

Runtime меняется чаще остальных частей.

Отделение улучшает кэширование.


Module IDs

Webpack может генерировать стабильные идентификаторы модулей.

optimization: {
  moduleIds: 'deterministic'
}

Chunk IDs

optimization: {
  chunkIds: 'deterministic'
}

Это уменьшает churn хешей между сборками.


Content Hashing

output: {
  filename: '[name].[contenthash].js'
}

Хеш зависит от содержимого файла.


Cache Busting

При изменении содержимого:

app.2ab45.js

становится:

app.77ce1.js

Браузер загружает новую версию.


RealContentHash

optimization: {
  realContentHash: true
}

Webpack вычисляет хеш после финальной генерации файла.

Это уменьшает ложные изменения хешей.


Module Concatenation

Scope Hoisting уменьшает накладные расходы модульной системы.

optimization: {
  concatenateModules: true
}

До оптимизации

(function(module){
  module.exports = ...
})

Каждый модуль изолирован.


После concatenation

Webpack объединяет несколько модулей в одну область видимости.

Это:

  • уменьшает overhead;
  • ускоряет выполнение;
  • уменьшает размер бандла.

Remove Available Modules

optimization: {
  removeAvailableModules: true
}

Удаляет дублирующиеся зависимости из дочерних чанков.


Merge Duplicate Chunks

optimization: {
  mergeDuplicateChunks: true
}

Webpack объединяет идентичные чанки.


Flag Included Chunks

optimization: {
  flagIncludedChunks: true
}

Webpack отмечает дочерние чанки, уже содержащиеся в родительских.


usedExports

optimization: {
  usedExports: true
}

Webpack отмечает используемые exports.

Это фундамент tree shaking.


innerGraph

optimization: {
  innerGraph: true
}

Более глубокий анализ зависимостей внутри модулей.


providedExports

optimization: {
  providedExports: true
}

Webpack анализирует, какие exports предоставляет модуль.


emitOnErrors

optimization: {
  emitOnErrors: false
}

При ошибке сборки ассеты не будут записаны.


removeEmptyChunks

optimization: {
  removeEmptyChunks: true
}

Удаляются пустые чанки.


avoidEntryIife

optimization: {
  avoidEntryIife: true
}

Webpack избегает лишнего IIFE вокруг entry.


Минификация HTML

Для HTML часто используется:

npm install html-minimizer-webpack-plugin --save-dev

Конфигурация

const HtmlMinimizerPlugin = require(
  'html-minimizer-webpack-plugin'
);

module.exports = {
  optimization: {
    minimizer: [
      new HtmlMinimizerPlugin()
    ]
  }
};

Оптимизация SVG

SVG можно:

  • минифицировать;
  • объединять;
  • преобразовывать в sprite.

Популярный инструмент:

npm install svgo --save-dev

PurgeCSS

Удаление неиспользуемых CSS-классов.

npm install purgecss-webpack-plugin --save-dev

Проблема больших CSS-файлов

UI-фреймворки часто генерируют огромный CSS:

styles.css → 4 MB

Используется лишь малая часть.


Работа PurgeCSS

PurgeCSS:

  1. анализирует шаблоны;
  2. ищет используемые классы;
  3. удаляет остальные.

DefinePlugin

Подмена констант на этапе сборки.

new webpack.DefinePlugin({
  __DEV__: JSON.stringify(false)
})

Dead Code Elimination

if (__DEV__) {
  console.log('debug');
}

После подстановки:

if (false) {
  console.log('debug');
}

Terser удаляет блок полностью.


EnvironmentPlugin

new webpack.EnvironmentPlugin([
  'NODE_ENV'
]);

Автоматически подставляет env-переменные.


Порядок выполнения оптимизаций

Типичный pipeline production-сборки:

  1. Loader transforms
  2. Tree shaking
  3. Chunk graph optimization
  4. Scope hoisting
  5. Minification
  6. Asset optimization
  7. Compression
  8. Hash generation
  9. Emit files

Production Mode

mode: 'production'

Webpack автоматически включает:

  • TerserPlugin;
  • tree shaking;
  • module concatenation;
  • side effect analysis;
  • deterministic ids.

Development Mode

mode: 'development'

Webpack отключает тяжёлые оптимизации ради скорости.


Типичная production-конфигурация

const TerserPlugin = require(
  'terser-webpack-plugin'
);

const CssMinimizerPlugin = require(
  'css-minimizer-webpack-plugin'
);

module.exports = {
  mode: 'production',

  devtool: 'source-map',

  optimization: {
    minimize: true,

    minimizer: [
      new TerserPlugin({
        parallel: true
      }),

      new CssMinimizerPlugin()
    ],

    splitChunks: {
      chunks: 'all'
    },

    runtimeChunk: 'single',

    moduleIds: 'deterministic',
    chunkIds: 'deterministic'
  },

  output: {
    filename: '[name].[contenthash].js',
    clean: true
  }
};

Типичные ошибки постобработки

Агрессивная минификация

Некоторые библиотеки ломаются после mangle.

Особенно:

  • reflection-based;
  • legacy-код;
  • AngularJS;
  • DI-контейнеры.

Неправильный sideEffects

{
  "sideEffects": false
}

может удалить важные импорты CSS.


Огромные source maps

Иногда .map больше самого бандла.

Причины:

  • inline source maps;
  • eval-source-map;
  • огромные библиотеки.

Дублирование зависимостей

Возможна ситуация:

lodash@4.17
lodash@4.16

В результате:

  • увеличивается размер;
  • ухудшается caching;
  • растёт время загрузки.

Производительность постобработки

На крупных проектах наиболее затратны:

Этап Нагрузка
Terser CPU
Source Maps RAM + CPU
Image Optimization CPU
Bundle Analyzer RAM
PurgeCSS CPU

Способы ускорения

Кэширование

cache: {
  type: 'filesystem'
}

Параллельная обработка

parallel: true

Ограничение source maps

devtool: false

для production-серверов без необходимости debugging.


Вынос тяжёлых библиотек

externals: {
  react: 'React'
}

Постобработка и CI/CD

В CI-пайплайнах постобработка обычно включает:

  • production build;
  • lint;
  • bundle analysis;
  • gzip generation;
  • asset validation;
  • deployment preparation.

Проверка размера бандла

Часто внедряются лимиты:

performance: {
  maxEntrypointSize: 300000,
  maxAssetSize: 300000
}

Webpack выдаёт предупреждения при превышении.


Stats JSON

Webpack может экспортировать статистику сборки.

webpack --json > stats.json

Файл содержит:

  • граф модулей;
  • размеры;
  • чанки;
  • зависимости;
  • timing-информацию.

Автоматизация анализа

На основе stats.json строятся:

  • CI-отчёты;
  • performance dashboards;
  • bundle diff;
  • regression monitoring.

Современный production pipeline

Типичная цепочка выглядит так:

TypeScript
    ↓
Babel
    ↓
Tree Shaking
    ↓
Split Chunks
    ↓
Terser
    ↓
CSS Minimizer
    ↓
Image Optimization
    ↓
Hashing
    ↓
Gzip/Brotli
    ↓
CDN Deployment