1. Введение
Create React App (далее - CRA) был хорошим другом. В 2018 году.
Он убрал боль с конфигурацией Webpack (т.к. было два варианта - npx create-react-app my-app или настраивать Webpack самому), дал тебе надежную сборку (которая запускалась одной командой - npm start) и ты просто работал. Но в 2025 году этот друг стал тем, кто приходит в гости на час, а остаётся на три дня. Медленный старт. Медленный Hot Module Replacement (HMR). Устаревшие инструменты сборки (Webpack 5 + Babel). И ощущение, что ты тащишь телегу сам вместо того чтобы ехать.
Мы мигрировали шестилетний боевой React веб проект на Vite. Не ради чего-то такого - ради того, чтобы перестать смотреть на спиннер на localhost, каждый раз когда нажимаешь Cmd/Ctrl+S.
Вот что нас ждало по дороге - и что мы получили в конце.
2. Почему вообще уходить с CRA?
Для понимания масштаба:
Примечание: Лягушка в закипающей воде. С каждой фичей сборка медленнее, но на доли секунды.
Это не катастрофа. Это просто медленно. И когда делаешь это 200 раз в день - считай часы жизни, которые уходят в никуда.
CRA больше не получает активной поддержки. Под капотом куча решений (Webpack 5, Babel), которые никто не торопится обновлять. Экосистема React давно двигается в сторону Vite, и игнорировать это становится всё сложнее.
Решение принято - мигрировать. Звучит просто. Оказалось - нет.
3. Проблемы, которые нас встретили
3.1 Переменные окружения: REACT_APP_* → VITE_*
Первое, что сломалось это .env файлы. Полностью.
CRA читает только переменные с префиксом REACT_APP_ и отдаёт их через process.env. Vite работает иначе: ему нужен префикс VITE_, а доступ - через import.meta.env.
Если у тебя десятки переменных разбросаны по проекту - готовьтесь к поиску и замене. Плюс все .env, .env.production, .env.staging нужно переименовывать. Автоматизируется скриптом, но сначала нужно это понять:
3.2 Проблемы с импортами SCSS
Vite не понимает CRA-специфичные пути вида @import './src/_mixins'. Он не знает, как сопоставлять такой путь - и просто вылетает с ошибкой.
Решение: настроить additionalData или includePaths в vite.config.js:
Либо заменить все проблемные импорты на относительные пути. Второй вариант чище, первый - быстрее, если импортов много.
3.3 Устаревший синтаксис SASS: @import и if()
SASS давно объявил @import устаревшим в пользу @use и @forward (https://sass-lang.com/documentation/breaking-changes/import/). В CRA это ещё прощалось - предупреждения были при сборке, но всё работало. В Vite с обновленным SASS они начинают мешать сборке.
Та же история с функцией if() - синтаксис изменился, и старые конструкции вызывают предупреждения об устаревшем.
Можно скрыть ошибки через silenceDeprecations в конфигурации - быстрая правка. Но лучше пройтись и мигрировать: SASS предоставляет sass-migrator (https://www.npmjs.com/package/sass-migrator) для автоматической замены @import на @use.
3.4 react-pdf и pdfjs-dist: специфичные внутренности Webpack
Это была самая неприятная проблема. react-pdf внутри использует entry.webpack - путь, который существует только в webpack-окружении. В Vite он просто не сопоставляется.
Web worker нужно либо копировать в public/ и указывать путь явно, либо использовать import.meta.url для динамической ссылки. Документация react-pdf для Vite есть, но найти её с первого раза это отдельный квест (https://docs.react-pdf-kit.dev/introduction/basic-usage/#vite).
3.5 Ошибки импорта SVG
В CRA SVG можно было импортировать двумя способами одновременно:
В Vite по умолчанию любой .svg - это просто URL. Для компонентного импорта нужен vite-plugin-svgr (https://www.npmjs.com/package/vite-plugin-svgr). Но есть нюанс - даже с этим плагином нужно правильно настроить, чтобы оба формата работали:
3.6 Стилизованные компоненты
Если в проекте использовались styled-components/macro - они не работают в Vite напрямую, так как макросы это Webpack/babel-macro механизм.
В большинстве случаев простая замена импорта решает проблему. Если использовался css helper из macro - тоже самое. Для более сложных случаев есть babel-plugin-styled-components (https://www.npmjs.com/package/babel-plugin-styled-components) через @vitejs/plugin-react с опцией babel.
7. Псевдонимы путей: @/components
Пути типа @/components/Button не работают из коробки ни в Vite, ни в ESLint, ни в VSCode. Нужно настроить три места синхронно:
Три файла. Один псевдоним. Если забудешь хотя бы один - будет работать, но с красными подчёркиваниями или ошибками ESLint.
8. require() → import()
Vite работает на стандартных ES-модулях. Динамические require() это не его язык.
Статические require() решаются простой заменой на import. Динамические - на import() с await. Сложнее всего, когда require() встречается в устаревших утилитах или сторонних пакетах, тогда там нужно или латать, или искать альтернативу.
3.9 Замена Jest на Vitest
Jest и Vite плохо уживаются вместе. Jest работает на CommonJS, а Vite на ES-модулях. Настроить Jest для работы с Vite проектом можно, но это боль с трансформерами и конфигурациями.
Vitest (https://vitest.dev/) - родное решение для Vite. Стиль взаимодействия практически идентичен Jest, миграция в большинстве случаев сводится к следующему:
А также замена импортов в тестах:
Большинство тестов просто заработают. Исключение - тесты с использованием снимков и специфичные для Jest функции сравнения: их нужно проверить отдельно.
4. Что мы получили в итоге
После того как всё заработало, пора посмотреть на цифры и они говорят сами за себя.
4.1 Холодный старт:
CRA: ~35 секунд
Vite: ~1.5 секунды
HMR (обновление после Cmd/Ctrl+S):
CRA: 3-8 секунд
Vite: ~100-300ms - браузер обновляется быстрее, чем ты успеваешь отпить кофе
4.2 Размер сборки: примерно тот же, но Vite лучше очищает дерево зависимостей по умолчанию.
4.3 Опыт разработки:
- Ошибки показываются прямо в браузере с понятной историей вызовов;
- HMR точечный - меняется только тот модуль, который ты изменил, а не весь граф зависимостей;
- Конфигурация читаемая и компактная - vite.config.ts на 40 строк против webpack.config.js на 300;
- Можно использовать TypeScript из коробки, без дополнительных плагинов;
import.meta - стандарт, который работает везде, не только в Webpack;
4.4 Экосистема:
Vite сегодня это стандарт для новых React-проектов. Remix, SvelteKit, SolidStart, Astro - все используют Vite, а значит: много документации, больше плагинов, ошибки исправляются быстрее.
5. Стоит ли оно того?
Да! Но если честно - миграция не за пару выходных.
Если проект небольшой и без тяжелых зависимостей, то скорее всего управитесь за день-два. Если есть PDF, SVG-компоненты, макросы, сложные SCSS-структуры и куча написанных тестов, то закладывайте неделю и тестируйте тщательно.
Главное: не мигрируйте и разрабатывайте новое одновременно в проекте. Сначала миграция, затем стабилизация/отладка и только потом новый функционал. Иначе непонятно, что сломалось - Vite или новый код.
Каждая из этих проблем решаемая. Некоторые - за 5 минут, некоторые - за полдня. Но когда всё работает, и ты нажимаешь “Сохранить” и видишь изменение в браузере через 200 миллисекунд, то понимаешь, что оно стоило каждой потраченной минуты.
CRA дал нам хороший старт. Vite даёт нам скорость для работы сегодня!

