Från Parcel till Vite: En kort berättelse om en 100K LOC migration
Vi har migrerat våra tre frontendprojekt från Parcel till Vite, och processen var... smidig.
Bakgrundshistorien
Vi har tre huvudsakliga frontendprojekt på Logto: inloggningsupplevelsen, konsolen och liveförhandsvisningen. Dessa projekt är alla i TypeScript, React och SASS-moduler; totalt har de runt 100K kodrader.
Vi älskade Parcel för dess enkelhet och nollkonfigurationsinställning. Jag kan fortfarande minnas dagen när jag blev chockad över hur enkelt det var att sätta upp ett nytt projekt med Parcel. Du kan bara köra parcel index.html
och boom, alla nödvändiga beroenden är installerade och projektet körs. Om du är en "erfaren" utvecklare kan du känna samma sak och jämföra det med de gamla dagarna av att ställa in med Gulp och Webpack. Parcel är som en magisk trollstav.
Parcels enkelhet var den främsta anledningen till att vi stannade med det så länge, även om det kunde vara lynnigt ibland. Till exempel:
- Parcel misslyckades ibland med att paketera projektet eftersom det inte kunde hitta några chunkfiler som faktiskt fanns där.
- Det behövdes några krångliga konfigurationer för att få det att fungera med vår monorepo-inställning.
- Det stöder inte MDX 3 inhemskt, så vi var tvungna att skapa en anpassad omvandlare för det.
- Det stöder inte manuella chunkar (vid tidpunkten för skrivandet är funktionen för manuella chunkar fortfarande i experimentstadiet), vilket är okej i de flesta fall, men ibland behöver du det.
Så varför beslutade vi oss för att migrera till något annat?
- Vi satt fast med Parcel 2.9.3, som släpptes i juni 2023. Varje gång en ny version släpptes efter det försökte vi uppgradera, men det misslyckades alltid med byggfel.
- Den senaste versionen av Parcel var 2.12.0, släppt i februari 2024. Trots att det har nästan dagliga commits har ingen ny release gjorts sedan dess.
Någon öppnade till och med en diskussion för att fråga om Parcel är död. Det officiella svaret är nej, Parcel är fortfarande vid liv, men det är i ett vi-jobbar-på-en-stor-omvandling-och-har-inte-tid-för-mindre-releaser tillstånd. För oss är det som en "ankdöd": Den senaste versionen vi kan använda är från mer än ett år sedan, och vi vet inte när nästa version kommer att släppas. Det ser ut som om det är dött, det agerar som om det är dött, så det är dött för oss.
Varför Vite?
Vi kände till Vite från Vitest. För flera månader sedan var vi trötta på Jests ESM-stöd (i tester) och ville prova något nytt. Vitest vann våra hjärtan med det inhemska ESM-stödet och Jest-kompatibilitet. Det har en fantastisk utvecklarupplevelse och drivs av Vite.
Nuvarande läget
Du kanske har olika inställningar i ditt projekt, men vanligtvis hittar du pluginersättningar då Vite-ekosystemet blommar. Här är våra inställningar vid migrationstillfället:
- Monorepo: Vi använder PNPM (v9) arbetsytor för att hantera vår monorepo.
- Modul: Vi använder ESM-moduler för alla våra projekt.
- TypeScript: Vi använder TypeScript (v5.5.3) för alla våra projekt med sökvägsalias.
- React: Vi använder React (v18.3.1) för alla våra frontendprojekt.
- Stil: Vi använder SASS-moduler för styling.
- SVG: Vi använder SVGs som React-komponenter.
- MDX: Vi har MDX med GitHub Flavored Markdown och Mermaid-stöd.
- Lazy loading: Vi behöver lazy load vissa av våra sidor och komponenter.
- Komprimering: Vi producerar komprimerade tillgångar (gzip och brotli) för våra produktionsbyggnader.
Migrationen
Vi började migrationen genom att skapa ett nytt Vite-projekt och leka runt med det för att se hur det fungerar. Processen var smidig och den riktiga migrationen tog bara några dagar.
Stöd out-of-the-box
Vite har out-of-the-box stöd för monorepo, ESM, TypeScript, React och SASS. Vi behövde bara installera de nödvändiga plugins och konfigurationer för att få det att fungera.
Sökvägsalias
Vite har inbyggt stöd för sökvägsalias, till exempel, i vår tsconfig.json
:
Vi behövde bara lägga till samma upplösning i vår vite.config.ts
:
Observera att ersättningsvägen bör vara en absolut sökväg, medan den är relativ till projektroten. Alternativt kan du använda pluginen vite-tsconfig-paths för att läsa sökvägsaliasen från tsconfig.json
.
React Fast Refresh och HMR
Även om Vite har inbyggt stöd för HMR, krävs det att installera en plugin för att aktivera React Fast Refresh. Vi använde pluginen @vitejs/plugin-react som tillhandahålls av Vite-teamet och har utmärkt stöd för React-funktioner som Fast Refresh:
SVG som React komponent
Vi använder pluginen vite-plugin-svgr för att konvertera SVGs till React-komponenter. Det är så enkelt som att lägga till pluginen i Vite-konfigurationen:
Vi specificerade dock inte under vilken villkor SVG:erna skulle konverteras till React-komponenter, så alla importer konverterades. Pluginet erbjuder en bättre standardkonfiguration: konvertera endast SVG:er som importeras med .svg?react
-tillägget. Vi uppdaterade våra importer därefter.
SASS-moduler
Även om Vite har inbyggt stöd för SASS-moduler, finns det en sak vi behöver bry oss om: hur klassnamnen är formaterade. Det kan vara besvärligt för användare och våra integrationstester om klassnamnen inte är formaterade konsekvent. En enkel rad konfiguration i vite.config.ts
kan lösa problemet:
Förresten, Parcel och Vite har olika sätt att importera SASS-filer:
Syntaxen * as
, fungerar dock i Vite, men det kommer att orsaka förlust av modulariserade klassnamn när du använder dynamiska nycklar för att komma åt styles
-objektet. Till exempel:
MDX-stöd
Eftersom Vite utnyttjar Rollup under huven, kan vi använda det officiella pluginet @mdx-js/rollup för att stödja MDX såväl som dess plugins. Konfigurationen ser ut så här:
Pluginet remarkGfm
används för att stödja GitHub Flavored Markdown, och pluginet rehypeMdxCodeProps
används för att överföra props till kodblocken i MDX-filerna som Docusaurus gör.
Mermaid-stöd inom MDX
Vi skulle vilja använda Mermaid-diagram i våra MDX-filer som andra programmeringsspråk. Användningen bör vara lika enkel som andra kodblock:
Bör visas som:
Eftersom vår app stöder ljusa och mörka teman, kodade vi lite för att få Mermaid-diagrammen att fungera med det mörka temat. En React-komponent skapas:
useTheme
är en anpassad hook för att få det aktuella temat från kontexten. Biblioteket mermaid
importeras asynkront för att minska laddningsstorleken för den initiala sidladdningen.
För kodblocket i MDX-filen har vi en enhetlig komponent för att göra jobbet:
Slutligen definierar vi MDX-leverantören enligt följande:
Lazy loading
Detta är inte något speciellt för Vite, men det är ändå värt att nämna eftersom vi uppdaterade våra sidor för att använda lazy loading under migrationen, och inget bröt efter det.
React har en inbyggd funktion React.lazy
för att lazy load komponenter. Däremot kan det orsaka vissa problem när du itererar snabbt. Vi skapade ett litet bibliotek kallat react-safe-lazy för att lösa problemen. Det är en direkt ersättning för React.lazy
och en detaljerad förklaring finns i detta blogginlägg.
Komprimering
Det finns en cool plugin vid namn vite-plugin-compression för att producera komprimerade tillgångar. Den stöder både gzip och brotli-komprimering. Konfigurationen är enkel:
Manuella chunkar
En fantastisk funktion hos Vite (eller den underliggande Rollup) är de manuella chunkarna. Medan React.lazy
används för lazy loading komponenter, kan vi ha mer kontroll över chunkarna genom att specificera de manuella chunkarna för att bestämma vilka komponenter eller moduler som ska packas ihop.
Till exempel kan vi först använda vite-bundle-visualizer för att analysera buntstorleken och beroenden. Sedan kan vi skriva en korrekt funktion för att dela chunkarna:
Utvecklingsserver
Till skillnad från produktionsbyggnaden, kommer Vite INTE att paketera din källkod i utvecklingsläget (inklusive länkade beroenden i samma monorepo) och behandlar varje modul som en separat fil. För oss kommer webbläsaren att ladda hundratals moduler för första gången, vilket ser galet ut men det är faktiskt bra i de flesta fall. Du kan se diskussionen här.
Om det är något för dig, en alternativ-men-inte- perfekt lösning är att lista de länkade beroenden i optimizeDeps
-alternativet i vite.config.ts
:
Detta kommer "för-bunta" de länkade beroenden och göra utvecklingsservern snabbare. Fallgropen är att HMR kanske inte fungerar som förväntat för de länkade beroenden.
Dessutom använder vi en proxy som serverar de statiska filerna i produktion och proxyerar förfrågningar till Vitest-servern i utveckling. Vi har några specifika portar konfigurerade för att undvika konflikter, och det är också lätt att ställa in i vite.config.ts
:
Miljövariabler
Till skillnad från Parcel, använder Vite ett modernt sätt att hantera miljövariabler genom att använda import.meta.env
. Det kommer automatiskt att ladda .env
-filerna och ersätta variablerna i koden. Dock krävs det att alla miljövariabler prefixas med VITE_
(konfigurerbart).
Medan vi använde Parcel, ersatte det helt enkelt process.env
-variablerna utan att kontrollera prefixet. Så vi kom på en lösning med hjälp av define
för att göra migrationen enklare:
Detta gör att vi gradvis kan lägga till prefixet till miljövariablerna och ta bort fältet define
.
Slutsats
Det var det! Vi har framgångsrikt migrerat våra tre frontendprojekt från Parcel till Vite, och hoppas denna korta historia kan hjälpa dig med din migration. Här är hur konfigurationen ser ut i slutet: