От Parcel к Vite: Краткая история миграции 100K LOC
Мы перенесли наши три фронтенд проекта с Parcel на Vite, и процесс прошел... гладко.
Предыстория
У нас есть три основных фронтенд проекта в Logto: интерфейс входа, Консоль и предварительный просмотр в реальном времени. Все эти проекты написаны на TypeScript, React и SASS модулях; в сумме они содержат около 100K строк кода.
Нам нравился Parcel за его простоту и настройку без необходимости конфигурации. Я до сих пор помню день, когда был шокирован, насколько легко было создать новый проект с Parcel. Вы можете просто запустить parcel index.html
и вуаля, все необходимые зависимости установлены и проект работает. Если ты "опытный" разработчик, возможно, ты чувствуешь то же самое, сравнивая это с прежними днями когда нужно было настраивать Gulp и Webpack. Parcel — это настоящая волшебная палочка.
Простота Parcel была основной причиной, по которой мы оставались с ним так долго, несмотря на то, что иногда он был капризным. Например:
- Parcel иногда не удавалось объединить проект, поскольку он не мог найти некоторые файлы, которые на самом деле существовали.
- Parcel требовал некоторых хитрых конфигураций, чтобы работать с нашей монорепозиторием.
- Parcel не поддерживает MDX 3 на уровне ядра, поэтому нам пришлось создать для него собственный трансформатор.
- Parcel не поддерживает ручные чанки (на момент написания, функция ручных чанков все еще находится на экспериментальной стадии), что подходит для большинства ситуаций, но иногда это требуется.
Так почему же мы решили перейти на что-то другое?
- Мы застряли на версии Parcel 2.9.3, которая была выпущена в июне 2023 года. Каждый раз, когда выпускалась новая версия после этого, мы пытались обновиться, но всегда сталкивались с ошибками сборки.
- Последняя версия Parcel была 2.12.0, выпущенная в феврале 2024 года. Хотя почти ежедневно появляются новые коммиты, с тех пор не было выпущено ни одной новой версии.
Кто-то даже открыл обсуждение, чтобы спросить мертв ли Parcel. Официальный ответ - нет, Parcel все еще жив, но он находится в состоянии «мы работаем над крупным рефакторингом и у нас нет времени на минорные версии». Для нас это выглядит как «смерть утки»: последнюю версию, которую мы можем использовать, выпустили больше года назад, и мы не знаем, когда выйдет следующая версия. Кажется, что Parcel мертв, он ведет себя как мертвец, так что для нас он мертв.
Почему Vite?
Мы узнали о Vite от Vitest. Несколько месяцев назад мы устали от поддержки ESM в Jest (в тестировании) и решили попробовать что-то новое. Vitest покорил наши сердца благодаря поддержке ESM на уровне ядра и совместимости с Jest. У него потрясающий опыт для разработчиков, и его поддерживает Vite.
Текущее состояние
Возможно, у вас в проекте можно настроить по-разному, но обычно вы найдете плагиновые замены, так как экосистема Vite развивается. Вот наши настройки на момент миграции:
- Монорепозиторий: Мы используем рабочие области PNPM (v9) для управления нашей монорепозиторием.
- Модуль: Мы используем ESM модули для всех наших проектов.
- TypeScript: Мы используем TypeScript (v5.5.3) для всех наших проектов с псевдонимами путей.
- React: Мы используем React (v18.3.1) для всех наших фронтенд проектов.
- Стилизация: Мы используем SASS модули для стилизации.
- SVG: Мы используем SVG как компоненты React.
- MDX: У нас есть MDX с поддержкой GitHub Flavored Markdown и Mermaid.
- Ленивая загрузка: Нам нужно лениво загружать некоторые страницы и компоненты.
- Сжатие: Мы создаем сжатые файлы (gzip и brotli) для наших продакшн-сборок.
Миграция
Мы начали процесс миграции, создав новый проект на Vite, и поиграли с ним, чтобы понять, как он работает. Процесс прошел гладко, и реальная миграция заняла всего несколько дней.
Поддержка из коробки
Vite поддерживает монорепозитории, ESM, TypeScript, React и SASS из коробки. Нам нужно было только установить необходимые плагины и конфигурации, чтобы все заработало.
Псевдонимы путей
Vite имеет встроенную поддержку псевдонимов путей, например, в нашем tsconfig.json
:
Нам нужно было только добавить такое же разрешение в наш vite.config.ts
:
Обратите внимание, что путь для замены должен быть абсолютным, хотя он относительно корня проекта. Альтернативно, вы можете использовать плагин vite-tsconfig-paths, чтобы считывать псевдонимы путей из tsconfig.json
.
React Fast Refresh и HMR
Хотя Vite поддерживает HMR из коробки, для включения React Fast Refresh необходимо установить плагин. Мы использовали плагин @vitejs/plugin-react, который предоставлен командой Vite и отлично поддерживает такие возможности React, как Fast Refresh:
SVG как компонент React
Мы использу ем плагин vite-plugin-svgr, чтобы конвертировать SVG в компоненты React. Все, что нужно - добавить плагин в конфигурацию Vite:
Однако мы не указали, при каких условиях SVG должен быть преобразован в компоненты React, так что все импорты были конвертированы. Плагин предлагает лучшую конфигурацию по умолчанию: конвертировать только те SVG, которые импортированы с расширением .svg?react
. Мы обновили наши импорты соответственно.
SASS модули
Хотя Vite поддерживает SASS модули на уровне ядра, есть одно, на что нам нужно обратить внимание: как форматируются имена классов. Это может вызвать проблемы у пользователей и наши интеграционные тесты, если имена классов будут отформатированы непоследовательно. Одна строка конфигурации в vite.config.ts
может решить эту проблему:
Кстати, Parcel и Vite имеют разные способы импорта SASS файлов:
Синтаксис * as
, однако, работает в Vite, но он приведет к потере модульных имен классов, когда вы используете динамические ключи для доступа к объекту styles
. Например:
Поддержка MDX
Поскольку Vite использует Rollup под капотом, мы можем использовать официальный плагин @mdx-js/rollup, чтобы поддерживать MDX, а также его плагины. Конфигурация выглядит так:
Плагин remarkGfm
используется для поддержки GitHub Flavored Markdown, а плагин rehypeMdxCodeProps
- для передачи пропсов к блокам кода в файлах MDX, как это делает Docusaurus.
Поддержка Mermaid в рамках MDX
Мы хотим использовать диаграммы Mermaid в наших файлах MDX так же, как и другие языки программирования. Использование должно быть таким же простым, как и другие блоки кода:
Должно рендериться как:
Поскольку наше приложение поддерживает светлую и темную темы, мы немного закодировали, чтобы заставить диаграммы Mermaid работать с темной темой. Была создана компонента React:
useTheme
- это настраиваемый хук для получения текущей темы из контекста. Библиотека mermaid
импортируется асинхронно, чтобы уменьшить размер загрузки на начальной странице.
Для блока кода в файле MDX у нас есть единый компонент, который выполняет работу:
Наконец, мы определяем MDX провайдер следующим образом:
Ленивая загрузка
Это не что-то специфичное для Vite, но все же стоит упомянуть, так как мы обновили наши страницы для использования ленивой загрузки во время миграции, и после этого ничего не сломалось.
React имеет встроенную функцию React.lazy
для ленивой загрузки компонентов. Однако это может вызвать некоторые проблемы, когда вы быстро итератируете. Мы разработали небольшую библиотеку под названием react-safe-lazy, чтобы решить эти проблемы. Она является заменой для React.lazy
, и подробное объяснение можно найти в этом посте в блоге.
Сжатие
Существует аккуратный плагин под названием vite-plugin-compression, чтобы создавать сжатые файлы. Он поддерживает как gzip, так и brotli сжатие. Конфигурация проста:
Ручные чанки
Одна из отличных функций Vite (или Rollup, который лежит в его основе) - это ручные чанки. Пока React.lazy
используется для ленивой загрузки компонентов, мы можем иметь больше контроля над чанками, определяя ручные чанки, чтобы решить, какие компоненты или модули следует объединить вместе.
Например, мы можем сначала использовать vite-bundle-visualizer, чтобы проанализировать размер бандла и зависимости. Затем мы можем написать правильную функцию для разделения чанков:
Dev сервер
В отличие от продакшн-сборки, Vite НЕ будет объединять ваш исходный код в режиме разработки (включая связанные зависимости в той же монорепозиторие) и будет обрабатывать каждый модуль как файл. При этом, браузер загрузит сотни модулей впервые, что может выглядеть странно, но в большинстве случаев это совершенно нормально. Вы можете ознакомиться с обсуждением здесь.
Если для вас это проблема, альтернативное, но не идеальное решение - указать связанные зависимости в опции optimizeDeps
файла vite.config.ts
:
Это "предварительно объединит" связанные зависимости и ускорит работу dev сервера. Недостатком является то, что HMR может не работать так, как ожидалось, для связанных зависимостей.
Кроме того, мы используем прокси, который обслуживает статические файлы в продакшене и проксирует запросы на сервер Vitest в режиме разработки. У нас есть несколько настроенных портов для избежания конфликтов, и это также легко настроить в файле vite.config.ts
:
Переменные среды
В отличие от Parcel, Vite использует современный подход к обработке переменных среды с использованием import.meta.env
. Он автоматически подгружает .env
файлы и заменяет переменные в коде. Однако, это требует, чтобы все переменные среды были префиксированы VITE_
(можно настроить).
Когда мы использовали Parcel, он просто заменял переменные process.env
, не проверяя префикс. Поэтому мы придумали обходной путь, используя поле define
, чтобы облегчить миграцию:
Это позволяет нам постепенно добавлять префикс к переменным среды и удалять поле define
.
Заключение
Вот и все! Мы успешно перенесли наши три фронтенд проекта с Parcel на Vite и надеемся, что эта короткая история поможет вам с вашей миграцией. Вот как конфигурация выглядит в конце: