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

Van Parcel naar Vite: Een kort verhaal van een 100K LOC migratie

We hebben onze drie frontend-projecten gemigreerd van Parcel naar Vite en het proces verliep... soepel.

Gao
Gao
Founder

De achtergrond

We hebben drie belangrijkste frontend-projecten bij Logto: de inlogervaring, de Console en de live preview. Deze projecten zijn allemaal in TypeScript, React en SASS-modules; in totaal hebben ze ongeveer 100K regels code.

We hielden van Parcel vanwege de eenvoud en zero-config setup. Ik kan me nog steeds de dag herinneren dat ik geschokt was hoe eenvoudig het was om een nieuw project op te zetten met Parcel. Je kunt gewoon 'parcel index.html' uitvoeren en boom, alle benodigde afhankelijkheden worden geïnstalleerd en het project draait. Als je een "ervaren" ontwikkelaar bent, kun je hetzelfde voelen in vergelijking met de oude dagen van het opzetten met Gulp en Webpack. Parcel is als een toverstaf.

De eenvoud van Parcel was de belangrijkste reden waarom we er zo lang bij bleven, ook al kon het soms grillig zijn. Bijvoorbeeld:

  • Parcel faalde soms bij het bundelen van het project omdat het sommige chunk-bestanden niet kon vinden die er eigenlijk waren.
  • Het had enkele hacky configuraties nodig om te werken met onze monorepo-setup.
  • Het ondersteunt MDX 3 niet native, dus moesten we een aangepaste transformer ervoor maken.
  • Het ondersteunt geen handmatige chunks (vanaf het moment van schrijven is de functie voor handmatige chunks nog in de experimentele fase), wat oké is voor de meeste omstandigheden, maar soms heb je het nodig.

Dus waarom besloten we naar iets anders te migreren?

  1. We zaten vast met Parcel 2.9.3, die in juni 2023 werd uitgebracht. Elke keer dat er daarna een nieuwe versie werd uitgebracht, probeerden we te upgraden, maar het mislukte altijd met bouwfouten.
  2. De nieuwste versie van Parcel was 2.12.0, uitgebracht in februari 2024. Hoewel er bijna dagelijks commits zijn, is er sindsdien geen nieuwe release gemaakt.

Iemand heeft zelfs een discussie geopend om te vragen of Parcel dood is. Het officiële antwoord is nee, Parcel leeft nog, maar het bevindt zich in een staat van we-zijn-bezig-met-een-grote-herstructurering-en-hebben-geen-tijd-voor-kleine-releases. Voor ons is het als een "eendengedood": De nieuwste versie die we kunnen gebruiken is van meer dan een jaar geleden en we weten niet wanneer de volgende versie wordt uitgebracht. Het lijkt dood, het gedraagt zich als dood, dus het is dood voor ons.

Parcel upgrade pull requests

Vertrouw me, we hebben het geprobeerd.

Waarom Vite?

We kenden Vite van Vitest. Enkele maanden geleden waren we moe van Jest's ESM-ondersteuning (in tests) en wilden we iets nieuws proberen. Vitest won ons hart met de native ESM-ondersteuning en de Jest-compatibiliteit. Het heeft een geweldige ontwikkelaarservaring en het wordt aangedreven door Vite.

De status quo

Je hebt mogelijk andere instellingen in je project, maar meestal vind je plug-invervangingen naarmate het Vite-ecosysteem bloeit. Hier zijn onze configuraties op het moment van migratie:

  • Monorepo: We gebruiken PNPM (v9) werkruimten om onze monorepo te beheren.
  • Module: We gebruiken ESM-modules voor al onze projecten.
  • TypeScript: We gebruiken TypeScript (v5.5.3) voor al onze projecten met padaliassen.
  • React: We gebruiken React (v18.3.1) voor al onze frontend-projecten.
  • Styling: We gebruiken SASS-modules voor styling.
  • SVG: We gebruiken SVG's als React-componenten.
  • MDX: We hebben MDX met GitHub Flavored Markdown en Mermaid-ondersteuning.
  • Lui laden: We moeten enkele van onze pagina's en componenten lui laden.
  • Compressie: We produceren gecomprimeerde assets (gzip en brotli) voor onze productieversies.

De migratie

We begonnen de migratie door een nieuw Vite-project te maken en ermee te spelen om te zien hoe het werkt. Het proces verliep soepel en de daadwerkelijke migratie duurde slechts een paar dagen.

Ondersteuning uit de doos

Vite heeft out-of-the-box ondersteuning voor monorepo, ESM, TypeScript, React en SASS. We hoefden alleen de benodigde plug-ins en configuraties te installeren om het te laten werken.

Padaliassen

Vite heeft ingebouwde ondersteuning voor padaliassen, bijvoorbeeld in onze tsconfig.json:

We hoefden alleen dezelfde resolutie toe te voegen in onze vite.config.ts:

Let op dat het vervangingspad een absoluut pad moet zijn, terwijl het relatief is ten opzichte van de projectroot. Als alternatief kun je de vite-tsconfig-paths plugin gebruiken om de padaliassen uit de tsconfig.json te lezen.

React Fast Refresh en HMR

Hoewel Vite ingebouwde ondersteuning heeft voor HMR, is het vereist een plugin te installeren om React Fast Refresh mogelijk te maken. We gebruikten de @vitejs/plugin-react plugin die wordt aangeboden door het Vite-team en een geweldige ondersteuning heeft voor React-functies zoals Fast Refresh:

SVG als React-component

We gebruiken de vite-plugin-svgr plugin om SVG's om te zetten naar React-componenten. Het is zo eenvoudig als het toevoegen van de plugin aan de Vite-configuratie:

Echter, we hebben niet gespecificeerd onder welke conditie de SVG's moeten worden omgezet naar React-componenten, dus werden alle imports geconverteerd. De plugin biedt een betere standaardconfiguratie: alleen de SVG's omzetten die worden geïmporteerd met de .svg?react extensie. We hebben onze imports dienovereenkomstig bijgewerkt.

SASS-modules

Hoewel Vite ingebouwde ondersteuning heeft voor SASS-modules, is er één ding waar we rekening mee moeten houden: hoe de klassennamen worden geformatteerd. Het kan lastig zijn voor gebruikers en onze integratietests als de klassennamen niet consistent zijn geformatteerd. De één-regel configuratie in de vite.config.ts kan het probleem oplossen:

Overigens hebben Parcel en Vite verschillende smaken voor het importeren van SASS-bestanden:

De * as syntaxis werkt echter in Vite, maar het zal het verlies van gemoduleerde klassennamen veroorzaken wanneer je dynamische sleutels gebruikt om toegang te krijgen tot het styles object. Bijvoorbeeld:

MDX-ondersteuning

Aangezien Vite Rollup onder de motorkap gebruikt, kunnen we de officiële @mdx-js/rollup plugin gebruiken om MDX te ondersteunen evenals zijn plugins. De configuratie ziet er als volgt uit:

De remarkGfm plugin wordt gebruikt om GitHub Flavored Markdown te ondersteunen, en de rehypeMdxCodeProps plugin wordt gebruikt om de props door te geven aan de codeblokken in de MDX-bestanden zoals wat Docusaurus doet.

Mermaid-ondersteuning binnen MDX

We willen graag Mermaid-diagrammen gebruiken in onze MDX-bestanden als andere programmeertalen. Het gebruik zou net zo eenvoudig moeten zijn als andere codeblokken:

Moet gerenderd worden als:

Aangezien onze app lichte en donkere thema's ondersteunt, hebben we een beetje gecodeerd om de Mermaid-diagrammen te laten werken met het donkere thema. Een React-component is gemaakt:

useTheme is een aangepaste hook om het huidige thema uit de context te halen. De mermaid bibliotheek wordt asynchroon geïmporteerd om de laadtijd voor de initiële pagina te verminderen.

Voor het codeblok in het MDX-bestand hebben we een uniforme component om de taak uit te voeren:

Ten slotte definiëren we de MDX-provider als volgt:

Lui laden

Dit is niet specifiek voor Vite, maar het is toch vermeldenswaard aangezien we onze pagina's hebben geüpdatet om tijdens de migratie lui geladen te worden en er is niets gebroken nadien.

React heeft een ingebouwde React.lazy functie om componenten lui te laden. Echter, het kan enkele problemen veroorzaken wanneer je snel iteraties uitvoert. We hebben een kleine bibliotheek gemaakt genaamd react-safe-lazy om de problemen op te lossen. Het is een drop-in vervanging voor React.lazy en een gedetailleerde uitleg is te vinden in deze blogpost.

Compressie

Er is een nette plugin genaamd vite-plugin-compression om gecomprimeerde assets te produceren. Het ondersteunt zowel gzip als brotli compressie. De configuratie is eenvoudig:

Handmatige chunks

Een geweldige functie van Vite (of de onderliggende Rollup) is de handmatige chunks. Terwijl React.lazy wordt gebruikt voor het lui laden van componenten, kunnen we meer controle hebben over de chunks door de handmatige chunks te specificeren om te beslissen welke componenten of modules samen moeten worden gebundeld.

Bijvoorbeeld, we kunnen eerst vite-bundle-visualizer gebruiken om de bundelgrootte en afhankelijkheden te analyseren. Vervolgens kunnen we een geschikte functie schrijven om de chunks te splitsen:

Dev server

In tegenstelling tot de productiebuild, Vite zal je broncode NIET bundelen in de ontwikkelingsmodus (inclusief gelinkte afhankelijkheden in dezelfde monorepo) en elke module als een bestand behandelen. Voor ons zal de browser honderden modules laden voor de eerste keer, wat er gek uitziet maar in de meeste gevallen eigenlijk in orde is. Je kunt de discussie hier zien.

Als dit een probleem voor jou is, is een alternatieve-maar-niet-perfect oplossing om de gelinkte afhankelijkheden op te sommen in de optimizeDeps optie van de vite.config.ts:

Dit zal de gelinkte afhankelijkheden "voorbundelen" en de dev server sneller maken. Het nadeel is dat HMR mogelijk niet werkt zoals verwacht voor de gelinkte afhankelijkheden.

Daarnaast gebruiken we een proxy die de statische bestanden in productie bedient en de verzoeken doorstuurt naar de Vitest-server in ontwikkeling. We hebben enkele specifieke poorten geconfigureerd om conflicten te vermijden, en het is ook gemakkelijk in te stellen in de vite.config.ts:

Omgevingsvariabelen

In tegenstelling tot Parcel, gebruikt Vite een moderne aanpak om omgevingsvariabelen te verwerken door gebruik te maken van import.meta.env. Het zal automatisch de .env bestanden laden en de variabelen in de code vervangen. Echter, het vereist dat alle omgevingsvariabelen worden geprefixeerd met VITE_ (configureerbaar).

Toen we Parcel gebruikten, verving het simpelweg de process.env variabelen zonder het prefix te controleren. Dus we hebben een workaround bedacht door het define veld te gebruiken om de migratie gemakkelijker te maken:

Dit stelt ons in staat om geleidelijk het prefix toe te voegen aan de omgevingsvariabelen en het define veld te verwijderen.

Conclusie

Dat is het! We zijn erin geslaagd om onze drie frontend-projecten te migreren van Parcel naar Vite, en we hopen dat dit korte verhaal je helpt bij jouw migratie. Hier is hoe de configuratie er uiteindelijk uitziet: