Tree shaking в Rollup опирается на статический анализ ES-модулей и удаление неиспользуемых экспортов. Однако эффективность этого механизма напрямую зависит от наличия побочных эффектов в модулях. Побочные эффекты (side effects) — это любое поведение модуля, которое сохраняется при его импорте независимо от использования его экспортов.
Модуль считается содержащим побочные эффекты, если при его загрузке выполняется код, влияющий на внешнее состояние приложения или среды выполнения. Это может происходить даже при отсутствии использования его экспортов.
Типичные примеры:
window,
globalThis)// side-effectful-module.js
window.appVersion = "1.0.0";
export function sum(a, b) {
return a + b;
}
Даже если sum не используется, сам факт импорта может
изменить глобальное состояние.
Tree shaking в Rollup работает на уровне графа зависимостей и анализирует:
Однако при наличии побочных эффектов модуль не может быть безопасно удалён полностью, даже если его экспорт не используется.
import "./analytics.js"; // может быть оставлен даже без использования экспорта
Если analytics.js выполняет регистрацию трекинга при
загрузке, его удаление изменит поведение приложения.
В экосистеме JavaScript основным механизмом управления побочными
эффектами на уровне пакета является поле sideEffects в
package.json.
{
"name": "my-lib",
"sideEffects": false
}
Значение этого поля:
false — весь пакет считается без побочных эффектовПример с частичными побочными эффектами:
{
"sideEffects": [
"*.css",
"src/polyfills.js"
]
}
Rollup и плагины могут использовать эту информацию для более агрессивного удаления неиспользуемого кода.
Tree shaking требует, чтобы код был максимально предсказуемым:
// плохо для tree shaking
if (Math.random() > 0.5) {
doSomething();
}
Такой код нельзя безопасно удалить или оптимизировать, так как его выполнение зависит от runtime.
В контексте побочных эффектов важно различать:
Rollup может анализировать только статические случаи.
Для более точного tree shaking используются специальные комментарии, обозначающие чистоту вызова:
/*@__PURE__*/ createLogger();
Такая аннотация позволяет Rollup и минификаторам удалять вызов, если его результат не используется.
Пример:
const logger = /*#__PURE__*/ createLogger();
Если logger не используется дальше, весь вызов может
быть удалён.
Однако это работает только при соблюдении условий:
Особую сложность представляют модули, которые не содержат явных side effects, но вызывают их через импорты:
import "./polyfills.js";
import "./setupGlobals.js";
Даже если текущий модуль чистый, его зависимости могут быть нет.
Это создаёт цепочку влияния:
entry → moduleA → moduleB (side effects)
Rollup не может удалить moduleA, если через него
достигается побочный эффект.
Импорт CSS часто рассматривается как побочный эффект:
import "./styles.css";
CSS не экспортирует JS-значения, но его подключение влияет на внешний вид приложения.
Поэтому такие импорты почти всегда помечаются как side effects и не удаляются tree shaking-ом.
JSON-модули обычно считаются чистыми:
import data from "./data.json";
Однако сам факт подключения может быть оптимизирован только если данные действительно используются. Если импорт не применяется, Rollup может удалить его, но только при отсутствии side effects в цепочке.
При работе с CommonJS модулями ситуация усложняется:
require() выполняется динамическиconst lib = require("legacy-lib");
Такие модули часто полностью исключаются из tree shaking или обрабатываются консервативно.
Rollup применяет несколько стратегий:
Но всё это ограничено наличием потенциальных побочных эффектов.
Ключевой принцип:
если модуль может изменить поведение приложения при импорте — он сохраняется
Неправильная настройка sideEffects приводит к
критическим проблемам:
{
"sideEffects": false
}
При таком значении Rollup может удалить даже импорт, который на самом деле выполняет важную регистрацию.
Побочные эффекты не всегда связаны с импортом:
export function init() {
window.config = loadConfig();
}
Даже если init не вызывается, сам факт его существования
не создаёт side effects, но его вызов — да. Tree shaking работает на
уровне вызова, а не определения.
Rollup удаляет код на нескольких уровнях:
Но наличие side effects снижает гранулярность до уровня модуля.
Побочные эффекты являются ограничивающим фактором tree shaking. Они формируют границу между безопасной оптимизацией и сохранением поведения программы. Чем точнее описаны side effects, тем более агрессивно Rollup может удалять неиспользуемый код, уменьшая итоговый бандл и улучшая производительность загрузки.