• parcel
  • vite
  • js
  • esbuild
  • bundler
  • monorepo

Od Parcel do Vite: Krótka historia migracji 100K LOC

Przenieśliśmy nasze trzy projekty frontendowe z Parcel na Vite, a proces był... płynny.

Gao
Gao
Founder

Historia w tle

Mamy trzy główne projekty frontendowe w Logto: doświadczenie logowania, Konsolę i podgląd na żywo. Projekty te są napisane w TypeScript, React i modułach SASS; w sumie mają około 100 tysięcy linii kodu.

Uwielbialiśmy Parcel za jego prostotę i konfigurację zerową. Wciąż pamiętam dzień, kiedy byłem zszokowany, jak łatwo było uruchomić nowy projekt za pomocą Parcel. Można po prostu uruchomić parcel index.html i boom, wszystkie niezbędne zależności są załadowane, a projekt działa. Jeśli jesteś "doświadczonym" programistą, możesz czuć to samo, porównując go do dawnych dni konfigurowania z Gulp i Webpack. Parcel to jak magiczna różdżka.

Prostota Parcel była głównym powodem, dla którego trzymaliśmy się go tak długo, pomimo że czasem bywał kapryśny. Na przykład:

  • Parcel czasami nieudanie łączył projekt, ponieważ nie mógł znaleźć niektórych plików chunk, które tam faktycznie były.
  • Potrzebował pewnych hakerniczych konfiguracji, aby działał z naszym układem monorepo.
  • Nie obsługuje natywnie MDX 3, więc musieliśmy stworzyć własny transformator.
  • Nie obsługuje ręcznych chunków (na chwilę obecną funkcja ręcznych chunków jest nadal w fazie eksperymentalnej), co jest ok w większości przypadków, ale czasem tego potrzeba.

Dlaczego więc postanowiliśmy migrować do czegoś innego?

  1. Utknęliśmy na Parcel 2.9.3, który został wydany w czerwcu 2023 roku. Za każdym razem, gdy wydawano nową wersję po tym, próbowaliśmy się zaktualizować, ale zawsze kończyło się to błędami budowania.
  2. Najnowsza wersja Parcel to 2.12.0, wydana w lutym 2024 roku. Chociaż codziennie pojawiały się prawie codzienne commit-y, od tego czasu nie wydano żadnej nowej wersji.

Ktoś nawet otworzył dyskusję, pytając czy Parcel jest martwy. Oficjalna odpowiedź brzmi nie, Parcel nadal żyje, ale jest w stanie "pracujemy nad dużym refaktorem i nie mamy czasu na drobne wydania". Dla nas to jak "śmierć kaczki": najnowsza wersja, której możemy używać, pochodzi sprzed ponad roku, i nie wiemy, kiedy zostanie wydana następna wersja. Wygląda, jakby był martwy, działa tak, jakby był martwy, więc dla nas jest martwy.

Pull requests aktualizacji Parcel

Uwierz mi, próbowaliśmy.

Dlaczego Vite?

Poznaliśmy Vite dzięki Vitest. Kilka miesięcy temu, zmęczeni wsparciem ESM w Jest (w testowaniu), chcieliśmy spróbować czegoś nowego. Vitest zdobył nasze serca dzięki natywnemu wsparciu ESM i kompatybilności z Jest. Ma ono niesamowite doświadczenie programisty i jest napędzane przez Vite.

Stan rzeczy

Możesz mieć inne ustawienia w swoim projekcie, ale zwykle znajdziesz zamienniki wtyczek, ponieważ ekosystem Vite rozkwita. Oto nasze ustawienia na moment migracji:

  • Monorepo: Używamy przestrzeni roboczych PNPM (v9) do zarządzania naszym monorepo.
  • Moduł: Używamy modułów ESM we wszystkich naszych projektach.
  • TypeScript: Używamy TypeScript (v5.5.3) we wszystkich naszych projektach z aliasami ścieżek.
  • React: Używamy React (v18.3.1) we wszystkich naszych projektach frontendowych.
  • Stylowanie: Używamy modułów SASS do stylowania.
  • SVG: Używamy SVG jako komponentów React.
  • MDX: Posiadamy obsługę MDX z markdownem w stylu GitHuba i wsparciem dla Mermaid.
  • Ładowanie opóźnione: Musimy opóźniać ładowanie niektórych naszych stron i komponentów.
  • Kompresja: Produkujemy skompresowane zasoby (gzip i brotli) dla naszych buildów produkcyjnych.

Migracja

Zaczęliśmy migrację, tworząc nowy projekt Vite i testując go, aby zobaczyć, jak działa. Proces był płynny, a rzeczywista migracja zajęła tylko kilka dni.

Obsługa z pudełka

Vite ma wbudowaną obsługę monorepo, ESM, TypeScript, React i SASS. Musieliśmy jedynie zainstalować niezbędne wtyczki i konfiguracje, aby wszystko działało.

Alias ścieżek

Vite ma wbudowane wsparcie dla aliasów ścieżek, na przykład w naszym tsconfig.json:

Musieliśmy tylko dodać tę samą rozdzielczość do naszego vite.config.ts:

Zauważ, że ścieżka zastępująca powinna być ścieżką absolutną, podczas gdy jest ona względna do katalogu głównego projektu. Alternatywnie, możesz użyć wtyczki vite-tsconfig-paths, aby odczytać aliasy ścieżek z tsconfig.json.

React Fast Refresh i HMR

Chociaż Vite ma wbudowaną obsługę HMR, konieczne było zainstalowanie wtyczki, aby włączyć React Fast Refresh. Użyliśmy wtyczki @vitejs/plugin-react, którą dostarcza zespół Vite i która ma doskonałe wsparcie dla funkcji React, takich jak Fast Refresh:

SVG jako komponent React

Używamy wtyczki vite-plugin-svgr, aby konwertować SVG na komponenty React. Wystarczy dodać tę wtyczkę do konfiguracji Vite:

Jednak nie określiliśmy, w jakiej sytuacji SVG powinno być konwertowane na komponenty React, więc wszystkie importy zostały przekonwertowane. Wtyczka oferuje lepszą domyślną konfigurację: tylko SVG importowane z rozszerzeniem .svg?react będą konwertowane. Zaktualizowaliśmy odpowiednio nasze importy.

Moduły SASS

Chociaż Vite ma wbudowaną obsługę modułów SASS, jest jedna rzecz, na którą musimy zwrócić uwagę: jak formatowane są nazwy klas. Może to być problematyczne dla użytkowników i naszych testów integracyjnych, jeśli nazwy klas nie są formatowane konsekwentnie. Jednolinijkowa konfiguracja w vite.config.ts może rozwiązać ten problem:

Przy okazji, Parcel i Vite mają różne sposoby importowania plików SASS:

Składnia * as, jednak, działa w Vite, ale spowoduje utratę zmodularyzowanych nazw klas, kiedy używasz dynamicznych kluczy do dostępu do obiektu styles. Na przykład:

Obsługa MDX

Ponieważ Vite wykorzystuje Rollup pod spodem, możemy użyć oficjalnej wtyczki @mdx-js/rollup do obsługi MDX oraz jego wtyczek. Konfiguracja wygląda tak:

Wtyczka remarkGfm jest używana do obsługi markdownu w stylu GitHuba, a wtyczka rehypeMdxCodeProps jest używana do przekazywania props do bloków kodu w plikach MDX, tak jak Docusaurus robi.

Wsparcie dla Mermaid w MDX

Chcielibyśmy używać diagramów Mermaid w naszych plikach MDX jako innych języków programowania. Używanie powinno być tak proste, jak inne bloki kodu:

Powinny być renderowane jako:

Ponieważ nasza aplikacja obsługuje jasne i ciemne motywy, zakodowaliśmy trochę, aby diagramy Mermaid działały z motywem ciemnym. Stworzono komponent React:

useTheme to niestandardowy hook do pobierania bieżącego motywu z kontekstu. Biblioteka mermaid jest importowana asynchronicznie, aby zmniejszyć rozmiar ładowania strony początkowej.

Dla bloku kodu w pliku MDX mamy zdefiniowany uniwersalny komponent, który wykonuje zadanie:

Na końcu definiujemy dostawcę MDX w następujący sposób:

Ładowanie opóźnione

To nie jest coś specyficznego dla Vite, ale warto to wspomnieć, ponieważ podczas migracji zaktualizowaliśmy nasze strony, aby używały ładowania opóźnionego, i nic się nie zepsuło po tym.

React ma wbudowaną funkcję React.lazy, aby opóźniać ładowanie komponentów. Jednak może to powodować pewne problemy, gdy iterujesz szybko. Stworzyliśmy małą bibliotekę zwaną react-safe-lazy, aby rozwiązać te problemy. To zamiennik dla React.lazy, a szczegółowe wyjaśnienie można znaleźć w tym poście na blogu.

Kompresja

Jest schludna wtyczka o nazwie vite-plugin-compression, która pozwala produkować skompresowane zasoby. Obsługuje zarówno kompresję gzip, jak i brotli. Konfiguracja jest prosta:

Ręczne chunkowanie

Jedną z wielkich cech Vite (lub podstawy Rollup) jest ręczne chunkowanie. Podczas gdy React.lazy jest używany do opóźnionego ładowania komponentów, możemy mieć większą kontrolę nad chunkami, określając ręczne chunki, aby zdecydować, które komponenty lub moduły powinny być łączone razem.

Na przykład, możemy najpierw użyć vite-bundle-visualizer, aby przeanalizować rozmiar pakietu i zależności. Następnie możemy napisać odpowiednią funkcję do podziału chunków:

Serwer deweloperski

W przeciwieństwie do builda produkcyjnego, Vite NIE będzie łączyć twojego kodu źródłowego w trybie deweloperskim (w tym połączonych zależności w tym samym monorepo) i traktuje każdy moduł jako plik. Dla nas przeglądarka załaduje setki modułów po raz pierwszy, co wygląda szalenie, ale w większości przypadków to jest w porządku. Można zobaczyć dyskusję tutaj.

Jeśli to problem dla ciebie, alternatywnym, lecz nie idealnym rozwiązaniem jest wymienienie połączonych zależności w opcji optimizeDeps w vite.config.ts:

To "wstępnie połączy" połączone zależności i przyspieszy serwer deweloperski. Wadą jest to, że HMR może nie działać zgodnie z oczekiwaniami dla połączonych zależności.

Dodatkowo, używamy proxy, które serwuje statyczne pliki w produkcji i przekierowuje zapytania do serwera Vitest w trybie deweloperskim. Mamy skonfigurowane specyficzne porty, aby uniknąć konfliktów, co również jest łatwe do ustawienia w vite.config.ts:

Zmienne środowiskowe

W przeciwieństwie do Parcel, Vite stosuje nowoczesne podejście do obsługi zmiennych środowiskowych, używając import.meta.env. Automatycznie ładuje pliki .env i zastępuje zmienne w kodzie. Jednak wymaga, aby wszystkie zmienne środowiskowe były prefiksowane VITE_ (konfigurowalne).

Podczas używania Parcel-a, po prostu zastępował process.env bez sprawdzania prefiksu. Więc wymyśliliśmy rozwiązanie, używając pola define, aby ułatwić migrację:

To pozwala nam stopniowo dodawać prefiks do zmiennych środowiskowych i usuwać pole define.

Podsumowanie

To wszystko! Udało nam się pomyślnie przenieść nasze trzy projekty frontendowe z Parcel na Vite i mamy nadzieję, że ta krótka historia pomoże w twojej migracji. Oto jak wygląda konfiguracja na końcu: