Поле treeshake: тонкая настройка

В Rollup tree-shaking является одним из ключевых механизмов оптимизации, основанных на статическом анализе модулей ES. Поле treeshake в конфигурации управляет тем, насколько агрессивно и каким образом сборщик удаляет неиспользуемые части графа зависимостей.

Механизм tree-shaking в Rollup опирается на несколько факторов: структуру импортов/экспортов, чистоту модулей, наличие побочных эффектов и корректную аннотацию кода. Конфигурация treeshake позволяет управлять этими аспектами на уровне сборки.


Базовые формы настройки treeshake

Поле treeshake может принимать два основных вида значений:

Булево значение

export default {
  treeshake: true
}
export default {
  treeshake: false
}
  • true — включает стандартный алгоритм tree-shaking
  • false — полностью отключает удаление неиспользуемого кода

Отключение используется редко, в основном для диагностики или при работе с кодом, который ломается из-за побочных эффектов анализа.


Объектная форма конфигурации

Для тонкой настройки используется объект:

export default {
  treeshake: {
    moduleSideEffects: true,
    propertyReadSideEffects: true,
    tryCatchDeoptimization: true,
    unknownGlobalSideEffects: true
  }
}

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


moduleSideEffects: контроль побочных эффектов модулей

Суть параметра

moduleSideEffects определяет, считает ли Rollup импорт модуля потенциально “грязным” (имеющим побочные эффекты).

Возможные значения

  • true — каждый модуль потенциально имеет побочные эффекты
  • false — модули считаются чистыми, если явно не указано иное
  • функция (id) => boolean — точечный контроль по пути модуля

Пример

export default {
  treeshake: {
    moduleSideEffects: id => {
      if (id.includes('styles')) return true
      return false
    }
  }
}

Поведение

Если модуль считается “чистым”, Rollup может удалить его импорт даже если он не использует экспорт напрямую, но импорт присутствует в графе.

Если модуль “грязный”, он сохраняется целиком при любом импорте.


propertyReadSideEffects: побочные эффекты при чтении свойств

Назначение

Этот параметр управляет тем, считаются ли обращения к свойствам объектов потенциально имеющими побочные эффекты.

export default {
  treeshake: {
    propertyReadSideEffects: true
  }
}

Влияние на оптимизацию

  • true — доступ к свойствам может блокировать удаление кода
  • false — чтение свойств считается безопасным и не влияет на side effects

Типичный сценарий

export const obj = {
  get value() {
    console.log('side effect')
    return 42
  }
}

При включённых побочных эффектах чтения Rollup сохранит код, даже если obj.value нигде не используется явно.


tryCatchDeoptimization: влияние try/catch на оптимизацию

Суть проблемы

Блоки try/catch усложняют статический анализ, так как потенциальные ошибки могут скрывать выполнение побочных эффектов.

export default {
  treeshake: {
    tryCatchDeoptimization: true
  }
}

Поведение

  • true — при наличии try/catch Rollup снижает уровень оптимизации внутри блока
  • false — Rollup продолжает агрессивную оптимизацию даже внутри try/catch

Практическое значение

Включённое значение повышает безопасность, но уменьшает эффективность tree-shaking в сложных участках кода.


unknownGlobalSideEffects: работа с глобальными сущностями

Назначение

Этот параметр определяет, считать ли обращения к неизвестным глобальным переменным потенциально опасными.

export default {
  treeshake: {
    unknownGlobalSideEffects: true
  }
}

Поведение

  • true — любое обращение к неизвестным глобальным переменным считается побочным эффектом
  • false — Rollup предполагает, что такие обращения безопасны

Пример ситуации

console.log(window.SOME_GLOBAL)

Если глобальная переменная не определена в анализе, при включённом режиме Rollup может сохранить больше кода, чем необходимо.


Влияние treeshake на экспортную модель

Tree-shaking в Rollup тесно связан с тем, как интерпретируются экспортируемые сущности:

  • named exports анализируются точечно
  • default export может сохраняться целиком при неоднозначности
  • re-export цепочки учитываются при построении графа зависимостей

При агрессивной настройке treeshake возможно удаление даже промежуточных модулей, если они не влияют на итоговый граф выполнения.


Аннотации side effects в коде

Rollup поддерживает специальные комментарии, влияющие на treeshaking:

/*#__PURE__*/
const x = createSomething()

Такая аннотация сигнализирует, что вызов функции не имеет побочных эффектов и может быть удалён, если результат не используется.

Конфигурация treeshake определяет, насколько доверять этим аннотациям.


Взаимодействие treeshake с внешними модулями

Внешние зависимости (external) обычно исключаются из анализа, но их импорты всё равно влияют на структуру графа.

Особенности:

  • внешние модули не трогаются tree-shaking-ом
  • но их импорт может сохраняться или удаляться в зависимости от использования
  • side effects внутри external пакетов не анализируются глубоко

Поведение при динамических конструкциях

Tree-shaking ограничен статическим анализом. Следующие конструкции ухудшают оптимизацию:

  • eval
  • динамические require
  • вычисляемые импорты
  • мутирующие глобальные объекты

При таких случаях Rollup может:

  • отключать часть оптимизаций
  • сохранять больше кода “на всякий случай”
  • маркировать модуль как потенциально небезопасный

Типичные стратегии настройки treeshake

Максимально агрессивная оптимизация

export default {
  treeshake: {
    moduleSideEffects: false,
    propertyReadSideEffects: false,
    tryCatchDeoptimization: false,
    unknownGlobalSideEffects: false
  }
}

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


Баланс для приложений

export default {
  treeshake: {
    moduleSideEffects: id => !id.includes('polyfill'),
    tryCatchDeoptimization: true,
    unknownGlobalSideEffects: true
  }
}

Подходит для реальных приложений с внешними зависимостями и частично неконтролируемым кодом.


Консервативный режим

export default {
  treeshake: {
    moduleSideEffects: true,
    propertyReadSideEffects: true,
    tryCatchDeoptimization: true,
    unknownGlobalSideEffects: true
  }
}

Используется при сложных legacy-проектах, где безопасность важнее оптимизации.


Связь treeshake и качества входного кода

Эффективность treeshaking в Rollup напрямую зависит от качества исходного кода:

  • ES Modules вместо CommonJS
  • отсутствие скрытых side effects
  • явные экспорты
  • использование чистых функций
  • корректные /*#__PURE__*/ аннотации

Чем чище архитектура, тем более предсказуем результат работы treeshake.


Ограничения механизма

Несмотря на гибкость, tree-shaking не способен:

  • анализировать поведение во время выполнения
  • гарантированно определять все побочные эффекты
  • оптимизировать код с динамической структурой модулей
  • работать точно с CommonJS без дополнительных плагинов

Поэтому treeshake всегда является балансом между безопасностью и агрессивной оптимизацией.