← Назад к списку
ТехническаяFrontendSenior

Что делает сборщик (bundler)? Объясните tree shaking и code splitting — как уменьшить бандл приложения.

Короткий ответ

  • Сборщик строит граф модулей и выдаёт оптимизированные бандлы
  • Tree shaking выбрасывает неиспользуемые экспорты
  • Работает благодаря статической природе ES-модулей
  • Side effects и CommonJS мешают вытряхиванию кода
  • Code splitting режет бандл на чанки по маршрутам
  • Динамический import() — точка разреза и ленивая загрузка
  • Анализ бандла показывает, что реально тянет вес

Сборщик превращает граф модулей в минифицированные чанки; tree shaking статически удаляет мёртвые экспорты ES-модулей, а code splitting через динамический import откладывает загрузку кода до момента необходимости.

Как сказать вслух

пример ответа

Сборщик берёт точку входа, обходит все импорты, строит граф модулей и на выходе даёт оптимизированные файлы: с транспиляцией, минификацией и хешами для кэширования. Tree shaking — это удаление кода, который никто не импортирует: оно возможно, потому что импорты и экспорты ES-модулей статичны и видны без запуска кода. Code splitting — разрезание бандла на части: код тяжёлой страницы загружается только когда пользователь на неё перешёл, обычно через динамический import. Вместе это главные инструменты уменьшения начальной загрузки.

Подробный ответ

Основной ответ

Сборщик решает несколько задач: резолв и объединение модулей, прогон через трансформации (TypeScript, JSX), минификация, хеширование имён для долгого кэширования, выдача source maps. Tree shaking опирается на статический анализ ESM: import/export нельзя сформировать динамически, поэтому сборщик точно знает, какие экспорты не используются, и удаляет их. Мешают этому CommonJS (require анализируется плохо) и побочные эффекты на уровне модуля — код, выполняющийся при импорте; поле sideEffects: false в package.json разрешает агрессивное удаление, а импорт всей библиотеки вместо конкретных функций сводит экономию на нет. Code splitting строится на динамическом import(), который возвращает промис и становится границей чанка: типично делят по маршрутам (в React — lazy + Suspense), по тяжёлым виджетам (графики, редакторы) и выносят общие зависимости в отдельные чанки. Современный фон: Vite в dev-режиме отдаёт нативные ES-модули без сборки (отсюда мгновенный старт), а прод собирает Rollup; esbuild и SWC ускорили трансформации на порядок.

Ключевые моменты

  • Почему ESM. Статичность import/export позволяет доказать неиспользуемость кода без его выполнения; с CommonJS это почти невозможно.
  • Side effects. Код, выполняющийся при импорте модуля, нельзя безопасно удалить; sideEffects в package.json — подсказка сборщику.
  • Границы чанков. Динамический import() задаёт точки разреза; деление по маршрутам — первый и самый выгодный шаг.
  • Измерение. Визуализаторы бандла (rollup-plugin-visualizer и аналоги) показывают тяжёлые зависимости — часто одна библиотека дат или иконок весит больше всего кода приложения.

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

Реальный сценарий: бандл вырос, LCP просел — нужно найти причину и порезать. Интервьюер ждёт конкретики: анализ состава бандла, замена тяжёлых зависимостей, ленивые маршруты, проверка, что библиотека поставляет ESM. Для senior-позиции полезно понимать и границы: чрезмерное дробление на чанки создаёт каскады запросов, поэтому критичные чанки префетчат или объединяют.

Частые ошибки

  • Говорят «tree shaking удаляет неиспользуемый код» без понимания, почему для этого нужны ES-модули
  • Импортируют библиотеку целиком и ждут, что сборщик «сам разберётся»
  • Дробят приложение на десятки мелких чанков, получая водопад сетевых запросов вместо ускорения

ИП Кочкин Алексей Сергеевич · ИНН 390509026279 · ОГРНИП 325390000030973 · jiniys2005@yandex.ru