Настройки treeshake.moduleSideEffects

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

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


Базовое поведение tree-shaking без moduleSideEffects

При стандартной работе Rollup анализирует:

  • экспортируемые символы
  • фактическое использование экспортов
  • цепочку импортов

Но без дополнительной информации Rollup вынужден считать, что любой модуль может иметь побочные эффекты, особенно если это нельзя доказать статически. В результате:

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

Роль treeshake.moduleSideEffects

Параметр moduleSideEffects определяет, как Rollup оценивает наличие побочных эффектов на уровне модулей.

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

true (значение по умолчанию)

Каждый модуль считается потенциально содержащим побочные эффекты.

Последствия:

  • Rollup не удаляет модули целиком, даже если их экспорты не используются
  • tree-shaking ограничивается удалением отдельных символов, но не модулей
  • безопасный, но менее агрессивный режим оптимизации

false

Все модули считаются чистыми (без побочных эффектов).

Последствия:

  • Rollup может удалять целые модули, если их экспорты не используются
  • максимальная агрессивность tree-shaking
  • высокий риск некорректного поведения при неверной оценке

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


'no-external'

Специфический режим, при котором:

  • внутренние модули проекта анализируются как обычно
  • внешние зависимости (node_modules) считаются не имеющими побочных эффектов

Это компромиссный вариант для проектов, где:

  • собственный код контролируется
  • внешние библиотеки уже оптимизированы или объявляют side effects через package.json

Функциональная форма (id, external) => boolean

Наиболее гибкий вариант, позволяющий задавать правила на уровне каждого модуля.

Аргументы:

  • id — путь к модулю
  • external — булево значение, указывающее, является ли модуль внешним

Пример логики:

  • разрешить побочные эффекты для файлов src/polyfills/**
  • считать чистыми утилиты src/utils/**
  • всегда учитывать side effects для CSS-импортов

Влияние на алгоритм tree-shaking

При анализе графа модулей Rollup выполняет несколько шагов:

  1. Строит dependency graph
  2. Определяет достижимые экспорты
  3. Проверяет наличие побочных эффектов у модулей
  4. Удаляет недостижимые узлы

moduleSideEffects влияет именно на шаг 3.

Если модуль считается «чистым»:

  • он может быть удалён полностью
  • даже если он импортирован, но его экспорты не используются

Если модуль считается «грязным»:

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

Связь с package.json sideEffects

Часто возникает пересечение с полем sideEffects в package.json, используемым webpack и частично Rollup-плагинами.

Однако логика различается:

Механизм Уровень Назначение
package.json sideEffects пакет помечает файлы с побочными эффектами
treeshake.moduleSideEffects Rollup глобальная политика анализа модулей

Взаимодействие

Если используется оба механизма:

  • package.json sideEffects даёт первичную информацию
  • moduleSideEffects может переопределять или уточнять поведение Rollup

Практические сценарии использования

Библиотечный код (UI, утилиты)

Для библиотек часто используется стратегия максимальной оптимизации:

  • большинство файлов — чистые функции
  • минимальные побочные эффекты

В этом случае допустимо:

  • включать 'no-external'
  • либо использовать функцию, помечающую только отдельные модули как side-effectful

Приложения с polyfills

Polyfill-модули часто:

  • расширяют глобальные объекты
  • выполняются при импорте

Пример:

  • core-js
  • regenerator-runtime

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


CSS и asset imports

Импорт стилей:

import './styles.css';

Даже если экспортов нет, сам факт импорта:

  • изменяет DOM (через plugin pipeline)
  • влияет на сборку

Такие файлы почти всегда должны быть отмечены как side-effectful через кастомную функцию.


Пример конфигурации Rollup

Агрессивная оптимизация

export default {
  treeshake: {
    moduleSideEffects: false
  }
};

Используется только если проект полностью контролируется и отсутствуют скрытые побочные эффекты.


Безопасный режим

export default {
  treeshake: {
    moduleSideEffects: true
  }
};

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


Компромиссный режим

export default {
  treeshake: {
    moduleSideEffects: 'no-external'
  }
};

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


Функциональная стратегия

export default {
  treeshake: {
    moduleSideEffects(id, external) {
      if (id.includes('polyfill')) return true;
      if (id.includes('utils')) return false;
      if (external) return false;
      return true;
    }
  }
};

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


Частые ошибки при настройке

Слишком агрессивное отключение side effects

Установка false без анализа проекта приводит к:

  • исчезновению polyfills
  • поломке библиотек, зависящих от глобального состояния
  • некорректной инициализации модулей

Игнорирование динамических эффектов

Некоторые эффекты невозможно определить статически:

  • регистрация плагинов
  • модификация window или globalThis
  • выполнение кода при импорте JSON/CSS через плагины

Rollup не всегда может это вывести автоматически.


Неправильная классификация внешних модулей

При использовании 'no-external' внешние зависимости считаются чистыми, но:

  • некоторые npm-пакеты имеют side effects по умолчанию
  • не все корректно описывают package.json sideEffects

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

Чем более точная настройка moduleSideEffects:

  • тем меньше итоговый бандл
  • тем выше стоимость анализа графа (незначительно)
  • тем больше зависимость от корректной конфигурации

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


Роль в архитектуре проекта

Правильная настройка moduleSideEffects фактически становится частью архитектурного контракта:

  • определяет границу «чистого» и «грязного» кода
  • влияет на модульность
  • формирует правила импорта

В крупных кодовых базах это часто дополняется соглашениями:

  • отдельные папки для side-effectful модулей
  • явная маркировка polyfills
  • запрет на скрытые глобальные эффекты

Диагностика проблем

Симптомы неправильной настройки:

  • код «исчезает» после сборки
  • стили не применяются
  • инициализация библиотек не выполняется
  • поведение отличается от dev-режима

Для диагностики полезно:

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