Webpack
Что это такое
Webpack — это сборщик модулей. Он стартует с одного или нескольких файлов точки входа, идёт по каждому импорту, применяет преобразования к файлам по пути, а на выходе получается несколько файлов, которые браузер умеет загружать. Именно Webpack сделал зависимости из npm пригодными для браузера; десять лет он оставался инструментом сборки по умолчанию для больших фронтенд-приложений.
Почему это важно
Модель Webpack стоит понимать, даже если вы никогда его не настраиваете. Терминология, которую он ввёл, стала общей для всех фронтенд-инструментов: точка входа, выход, загрузчик, плагин, разделение кода, чанк, хеширование, tree shaking. К тому же он до сих пор работает во множестве продакшн-проектов, и его конфигурация нередко оказывается тем местом, где сборка медленна или вывод неожиданно велик. Разберитесь, что делает каждая часть, и конфигурация на пятьсот строк перестанет быть заклинанием и станет тем, что можно починить.
Ключевые идеи
Весь смысл — в графе зависимостей
Webpack не загружает файлы в том порядке, в каком вы их перечислили, — он их находит. Файл точки входа импортирует другие, те в свою очередь импортируют ещё, и именно граф определяет вывод. Поэтому добавление импорта в любом месте меняет бандл; поэтому же случайный импорт большой библиотеки способен удвоить его.
// webpack.config.js
export default {
mode: "production",
entry: "./src/index.js",
output: { filename: "[name].[contenthash].js", clean: true },
};
Хеш содержимого в имени файла меняется только при изменении содержимого — именно это позволяет браузеру кэшировать бандл на год.
Загрузчики преобразуют, плагины расширяют
Загрузчик — функция, применяемая к содержимому файла до того, как он войдёт в граф: скомпилировать TypeScript, преобразовать таблицу стилей, встроить небольшое изображение. Плагин же подключается к жизненному циклу сборки и делает что-то более широкое: выпускает HTML-файл, копирует ресурсы, анализирует бандл. Если изменение касается отдельного файла, это загрузчик; если всей сборки — плагин.
Разделение кода — это конфигурация плюс import()
Динамический import() создаёт отдельный чанк, который запрашивается при выполнении. Так тяжёлый редактор или редко посещаемый маршрут остаётся вне начальной загрузки. Сборщик умеет и сам выделять общий код между точками входа — отсюда и берётся рантайм-чанк.
Разработка и продакшн — разные сборки
Разработке нужны быстрые пересборки и читаемый исходный код, продакшену — маленький вывод, минификация и tree shaking. mode переключает значения по умолчанию. Много сложности в конфигурации появляется как раз у проектов, которые пытались обслужить обе задачи одним набором настроек.
Сложность конфигурации — известная цена
Гибкость Webpack означает, что выразить можно почти любую сборку — и допустить почти любую ошибку. Большая конфигурация с несколькими точками входа, несколькими загрузчиками, сцепленными на каждый тип файла, и плагинами, которые правят друг друга: именно из-за неё многие команды перешли к инструментам с меньшим числом решений.
Практика
Проследите одну зависимость до бандла
Добавьте в файл точки входа импорт небольшой библиотеки, соберите проект и поищите её код в выводе. Затем импортируйте её динамически в ветви, которая выполняется редко, и сравните результат.
Готово, когда: первая сборка включает библиотеку в основной бандл, а вторая выпускает её отдельным чанком, который не запрашивается при загрузке.
Измерьте цену одного загрузчика
Возьмите рабочую конфигурацию и отключите загрузчик, который преобразует какой-нибудь тип файлов; затем пересоберите проект.
Готово, когда: вы можете сказать, что сломалось и преобразовывал ли загрузчик содержимое или лишь менял сообщение об ошибке при его отсутствии.
Частые ошибки
- Импорт целой библиотеки ради одной функции — в граф входит весь пакет; исправить: импортируйте конкретный модуль или выберите пакет поменьше.
- Продакшн на отладочной сборке — вывод не минифицирован, не хеширован и намного больше; исправить: собирайте с
mode: "production"и проверяйте артефакты. - Один загрузчик, настроенный дважды на пересекающиеся типы файлов — один молча побеждает, и преобразование будто не работает; исправить: держите одно правило на расширение и проверяйте разрешённую конфигурацию.
- Игнорирование отчёта о бандле — случайная зависимость удваивает загрузку, и никто не замечает; исправить: осматривайте вывод после добавления зависимости.
- Сложная конфигурация в проекте, которому она больше не нужна — сборку менять дольше, чем пользоваться ею; исправить: пересмотрите инструмент, когда требования сузились.
Что читать дальше
- JavaScript (02-frontend/06-javascript) — синтаксис модулей, который задаёт граф, по которому идёт сборщик.
- Управление пакетами (npm) (02-frontend/04-package-management-npm) — откуда берутся зависимости, которые он собирает.
- Vite (02-frontend/09-vite) — инструмент, который заменил его в большинстве новых проектов, и почему.