why-vite-is-fast.md — vim

Почему Vite быстрый — и что делать, если он всё равно тормозит

Как устроен dev-сервер на нативных модулях, зачем там esbuild и Rollup, и пять реальных причин, по которым Vite на проекте может еле шевелиться.


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

После переезда на Vite ожидание практически исчезло. Не магия, всё объяснимо.

Что делает классический бандлер

Перед тем как показать первую страницу, он строит граф всех модулей приложения и собирает бандл. Тысяча файлов - тысяча преобразований, и всё это до того, как ты увидишь первый пиксель. Чем больше проект, тем дольше старт. Линейно.

Что делает Vite

Опирается на то, что браузеры давно умеют в ES-модули:

<script type="module" src="/src/main.ts"></script>

Браузер просит main.ts, видит import, просит следующий файл. Dev-сервер отдаёт файлы по требованию и преобразует их на лету. Приложение целиком не собирается - собирается ровно то, что открыто на экране.

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

Отдельная история - node_modules. Пакетов много, меняются они редко, часть до сих пор в CommonJS. Их Vite один раз прогоняет через esbuild (он на Go и быстрее JS-бандлеров на порядки), складывает в node_modules/.vite и дальше отдаёт готовыми, с долгим кэшем.

В прод так нельзя

Нативные модули без сборки в проде - это сотни запросов и водопад импортов, поэтому для прода Vite собирает всё Rollup’ом: чанки, минификация, хэши в именах. То есть быстрый dev не покупается ценой худшего бандла - это разные конвейеры.

Сейчас команда переезжает на Rolldown, бандлер на Rust, чтобы конвейер стал один и прод собирался так же быстро. Пока это опциональная замена, но направление понятное: инструменты уходят в нативные языки, и ждать мы будем всё меньше.

Что это дало команде

Кроме очевидного «стало быстро», два эффекта, которые я не ожидал:

Люди стали чаще править мелочи в интерфейсе. Когда правка видна мгновенно, ты подвигаешь отступ и посмотришь. Когда пересборка восемь секунд - забьёшь.

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

Если тормозит всё равно

Пять причин, которые я реально разбирал на проектах:

Барели. index.ts, который реэкспортирует двести компонентов. Один импорт тянет всю библиотеку, потому что dev-сервер честно грузит все реэкспорты. Импортируй напрямую из файла.

Иконки как отдельные компоненты. Сотни модулей на странице. Спрайт или динамический импорт.

Глобальные @import препроцессора в каждом SFC. Одни и те же миксины перемалываются сотни раз.

Плагины, которые трогают каждый модуль. Каждый transform умножается на количество файлов. Смотри vite --profile.

Антивирус, следящий за node_modules. Звучит как анекдот, встречалось не раз, разница в разы. Windows-разработчики поймут.

Для прода полезно раз в пару месяцев глянуть на состав бандла:

npx vite-bundle-visualizer

Обычно там обнаруживается что-нибудь вроде moment со всеми локалями или lodash целиком ради трёх функций.

Если мигрируешь с webpack

Не переноси конфиг один в один. Процентов восемьдесят webpack-конфига - это обходные пути для проблем, которых в Vite просто нет. Начни с пустого vite.config.ts и добавляй только то, без чего проект не собирается. Быстрее и честнее.

Почитать