Tree Shaking в Webpack опирается на статический анализ модулей ES и
на явные сигналы о побочных эффектах кода. Без выполнения двух ключевых
условий — использования ESM и корректной настройки
sideEffects — механизм удаления неиспользуемого кода либо
работает частично, либо полностью отключается на уровне оптимизации.
Tree Shaking возможен только там, где система модулей позволяет
статический анализ зависимостей. ES Modules (ESM) предоставляют такую
возможность за счёт синтаксиса import и
export, который не зависит от выполнения кода.
ESM-модули анализируются до выполнения:
import { formatDate } from './date.js';
Такая конструкция однозначно определяет, что из модуля
date.js требуется только formatDate. Это
позволяет сборщику:
В отличие от ESM, CommonJS (require) не предоставляет
статической структуры:
const date = require('./date');
Значение date может быть вычислено динамически, что
делает невозможным точное определение используемых частей модуля без
выполнения кода.
При использовании CommonJS Webpack вынужден:
Даже при включённом optimization.usedExports
эффективность анализа резко снижается.
Tree Shaking активируется только в production-режиме или при явной настройке:
mode: 'production'
В этом режиме включаются:
TerserPlugin для удаления мёртвого кодаusedExports для анализа используемых экспортовconcatenateModules (scope hoisting)Однако даже при этих оптимизациях без ESM значительная часть потенциала Tree Shaking теряется.
Перед удалением кода Webpack должен определить, какие экспорты реально используются.
optimization: {
usedExports: true
}
Этот режим помечает экспорты как используемые или неиспользуемые, но не удаляет их физически. Результат выражается в аннотациях:
/* unused harmony export foo */
Эти метки затем используются минификатором для удаления кода.
Даже если экспорт не используется, модуль может выполнять побочные
действия при импорте. Именно для этого существует механизм
sideEffects.
Побочный эффект — это любое действие модуля, которое влияет на окружение вне системы модулей:
Пример:
// polyfill.js
window.fetch = function () {};
Даже если экспортов нет, сам факт импорта изменяет глобальное окружение.
Механизм Tree Shaking использует поле sideEffects в
package.json:
{
"sideEffects": false
}
Если указано false, Webpack считает:
Это максимальный уровень агрессивности Tree Shaking.
Часто полная чистота невозможна, особенно при наличии CSS:
{
"sideEffects": [
"*.css",
"*.scss"
]
}
В этом случае:
Без sideEffects Webpack не может безопасно удалить
импорт:
import './polyfills';
Даже если модуль не экспортирует ничего, он может:
Если флаг отсутствует, Webpack по умолчанию считает, что побочные эффекты есть.
Tree Shaking становится эффективным только при одновременном выполнении двух условий:
Если отсутствует хотя бы один компонент, оптимизация деградирует.
// math.js
export function sum(a, b) {
return a + b;
}
export function multiply(a, b) {
return a * b;
}
// unused function
export function debugLog() {
console.log('debug');
}
import { sum } from './math.js';
console.log(sum(2, 3));
Webpack:
summultiply и debugLog как
неиспользуемыеCSS является классическим примером побочных эффектов:
import './styles.css';
Такой импорт:
Поэтому CSS нельзя помечать как полностью tree-shakable код, но можно
включать в исключения sideEffects.
Дополнительный слой оптимизации усиливает Tree Shaking:
optimization: {
concatenateModules: true
}
Webpack объединяет модули в единый scope, уменьшая:
Это усиливает удаление мёртвого кода, особенно в связке с ESM.
Даже при идеальной настройке существуют ограничения:
const mod = await import('./' + name);
Такие конструкции разрушают статический анализ.
export const value = init();
Если init() имеет побочные эффекты, удаление становится
небезопасным.
export * from './moduleA';
Webpack должен развернуть цепочку экспортов, что усложняет анализ.
Tree Shaking проходит несколько этапов:
Каждый этап зависит от предыдущего, и отсутствие ESM или корректного
sideEffects прерывает цепочку оптимизации.
Современные библиотеки ориентируются на Tree Shaking:
sideEffects: falseТакой подход позволяет сборщику выполнять агрессивное удаление кода без риска изменения поведения приложения.