Урок по треку Инструменты 6 мин

Сборщики: Vite, webpack и что они делают

Зачем нужен сборщик, Vite и webpack, tree shaking, code splitting, source maps.

Зачем вообще сборщик

Ты пишешь сотни модулей на TypeScript и JSX, импортируешь CSS, картинки и пакеты из node_modules. Браузер так не умеет: TS и JSX он не понимает, голые импорты вида import React from 'react' без import map не резолвит, а сотни мелких файлов грузить долго. Сборщик:

  1. строит граф зависимостей от точки входа (main.tsx);
  2. трансформирует файлы: TS и JSX в JS, современный синтаксис под целевые браузеры, CSS через PostCSS;
  3. склеивает модули в несколько чанков и минифицирует их;
  4. выдаёт файлы с хешем содержимого в имени (index-3f9a1c.js), чтобы их можно было кешировать навсегда.

Vite и webpack

webpack собирает весь граф и в dev, и в проде. Всё настраивается через loaders (как превратить файл в модуль: ts-loader, css-loader) и plugins (что сделать с результатом: HTML, минификация, env). Гибкий, но на больших проектах dev-сервер стартует медленно.

Vite в dev вообще не бандлит твой код: браузер получает нативные ES-модули, Vite трансформирует каждый файл по запросу. Зависимости из node_modules один раз предварительно собираются (pre-bundling), чтобы не грузить тысячи файлов. Поэтому старт почти мгновенный. Для продакшена Vite делает полноценную сборку бандлером (исторически Rollup, в новых версиях Rolldown).

HMR (hot module replacement): при правке файла заменяется только он и его зависимые модули, страница не перезагружается, состояние React-компонентов обычно сохраняется.

Tree shaking

Удаление неиспользуемого кода. Работает, потому что import/export в ES-модулях статические: сборщик видит, что из lodash-es взяли только debounce, и выкидывает остальное.

// хорошо: статический именованный импорт, лишнее вырежется
import { debounce } from 'lodash-es';

// плохо: CommonJS, сборщик не знает, что реально используется
const _ = require('lodash');

Мешают tree shaking: CommonJS, побочные эффекты на уровне модуля (код, который выполняется при импорте), реэкспорт всего через export * в библиотеках без пометки. Поле "sideEffects": false в package.json библиотеки говорит сборщику: модули без использованных экспортов можно выбрасывать целиком.

Code splitting

Динамический import() создаёт отдельный чанк, который грузится по требованию:

const Editor = lazy(() => import('./Editor'));

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

Транспиляция и типы

esbuild и SWC превращают TS в JS очень быстро, потому что просто вырезают типы и не проверяют их. Проверку типов делают отдельно: tsc --noEmit в CI или в редакторе. Поэтому «собралось» не значит «типы правильные».

Source maps

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

Переменные окружения

В браузерный бандл значения окружения вшиваются на этапе сборки. В Vite в клиентский код попадают только переменные с префиксом VITE_ (import.meta.env.VITE_API_URL). Всё, что попало в бандл, видно любому пользователю, секретам там не место.

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

  • Импорт всей библиотеки ради одной функции.
  • Ждать, что esbuild поймает ошибку типов.
  • Класть секреты в переменные, доступные клиенту.
  • Отдавать index.html с долгим кешем: тогда пользователи не увидят новые хеши файлов.

Что спросят на собесе

  • Зачем нужен сборщик и что он делает с кодом.
  • Почему Vite быстрее webpack в разработке.
  • Что такое tree shaking и что ему мешает.
  • Как работает code splitting и ленивые импорты.
  • Зачем хеш в имени файла и что такое source maps.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.