Пометка конкретных файлов как имеющих побочные эффекты

Механизм sideEffects в экосистеме Webpack используется для точного указания того, какие модули в пакете можно безопасно удалять при tree shaking, а какие должны сохраняться независимо от того, используются ли их экспорты напрямую. Это один из ключевых инструментов оптимизации, позволяющий существенно уменьшать размер конечного бандла за счёт исключения неиспользуемого кода.

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

  • изменение глобальных объектов (window, globalThis)
  • регистрация полифиллов
  • модификация прототипов
  • подключение стилей
  • выполнение кода при импорте без экспорта значений

Пример:

// polyfill.js
import 'core-js/es/array';
import 'regenerator-runtime/runtime';

console.log('polyfills loaded');

Даже если этот файл не экспортирует ничего, его выполнение изменяет окружение. Такой модуль нельзя просто удалить как «неиспользуемый».

Проблема tree shaking без информации о side effects

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

Однако без явного указания возможны ошибки:

import './polyfills.js'; // важно для окружения

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

Именно для этого существует механизм sideEffects.

Поле sideEffects в package.json

Ключ sideEffects указывается в package.json и сообщает сборщику, какие файлы нельзя безопасно удалять при tree shaking.

Глобальное отключение побочных эффектов

Самый простой вариант:

{
  "name": "my-library",
  "sideEffects": false
}

Это означает:

  • все файлы пакета считаются «чистыми»
  • любой неиспользуемый импорт может быть удалён
  • Webpack может агрессивно выполнять tree shaking

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

Частичное указание файлов с побочными эффектами

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

{
  "sideEffects": [
    "*.css",
    "./src/polyfills.js",
    "./src/globalSetup.js"
  ]
}

Здесь:

  • CSS-файлы всегда считаются побочными
  • конкретные JS-файлы защищены от удаления
  • остальные модули считаются «чистыми»

Типичные случаи побочных эффектов

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

import './button.css';

CSS не экспортирует значений, но его импорт изменяет DOM через style-loader или извлекается в отдельный файл. Поэтому CSS почти всегда считается side effect.

Глобальная инициализация

// globalSetup.js
window.appVersion = '1.0.0';

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

Полифиллы

import 'whatwg-fetch';

Добавляет глобальные API, не имея экспортов.

Регистрация патчей

import './patchDate';
Date.prototype.toISO = function () {
  return this.toISOString();
};

Любая модификация прототипов является побочным эффектом.

Чистые модули и условия безопасного tree shaking

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

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

Пример:

export function sum(a, b) {
  return a + b;
}

export function multiply(a, b) {
  return a * b;
}

Такой модуль можно безопасно сокращать до используемых экспортов.

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

Tree shaking в Webpack основан на ESM (ES Modules), так как они обеспечивают статическую структуру импорта:

import { sum } from './math.js';

Если math.js помечен как без побочных эффектов, Webpack может:

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

Ошибки неправильной разметки sideEffects

Ложное указание false

{
  "sideEffects": false
}

Если внутри пакета есть:

import './register';

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

Отсутствие CSS в списке

Если не указать:

"sideEffects": ["*.css"]

CSS может быть исключён из сборки при агрессивной оптимизации.

Оптимизация через разделение модулей

Практика показывает, что лучше разделять код:

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

Пример структуры:

src/
  core/
    math.js
    string.js
  init/
    polyfills.js
    setupEnv.js
  styles/
    main.css

package.json:

{
  "sideEffects": [
    "./src/init/*.js",
    "./src/styles/*.css"
  ]
}

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

Корректная настройка sideEffects позволяет:

  • уменьшить размер итогового бандла
  • ускорить загрузку приложения
  • повысить эффективность tree shaking
  • снизить количество неиспользуемого кода в runtime

Webpack может точнее строить dependency graph, исключая ненужные ветви.

Связь с optimization.usedExports

Механизм sideEffects работает в связке с:

  • optimization.usedExports
  • mode: production
  • статическим анализом ESM

sideEffects отвечает за уровень файла, а usedExports — за уровень экспортов внутри модуля.

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

На практике применяют следующие подходы:

  • библиотеки → "sideEffects": false при полной уверенности
  • приложения → перечисление исключений (CSS, polyfills, setup-файлы)
  • смешанные проекты → точечная маркировка опасных модулей

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