Von Parcel zu Vite: Eine kurze Geschichte einer 100K LOC Migration
Wir haben unsere drei Frontend-Projekte von Parcel zu Vite migriert und der Prozess war... reibungslos.
Die Vorgeschichte
Wir haben drei Haupt-Frontend-Projekte bei Logto: die Anmeldeerfahrung, die Konsole und die Live-Vorschau. Diese Projekte sind alle in TypeScript, React und SASS-Modulen; insgesamt haben sie etwa 100K Zeilen Code.
Wir liebten Parcel aufgrund seiner Einfachheit und der Null-Konfigurations-Einrichtung. Ich erinnere mich noch an den Tag, als ich schockiert war, wie einfach es war, ein neues Projekt mit Parcel einzurichten. Du kannst einfach parcel index.html
ausführen und boom, alle notwendigen Abhängigkeiten werden installiert und das Projekt läuft. Wenn du ein "erfahrener" Entwickler bist, fühlst du vielleicht dasselbe und erinnerst dich an die alten Zeiten mit Gulp und Webpack. Parcel ist wie ein Zauberstab.
Die Einfachheit von Parcel war der Hauptgrund, warum wir so lange daran festgehalten haben, auch wenn es manchmal launisch war. Zum Beispiel:
- Parcel versagte manchmal beim Bündeln des Projekts, weil es einige Chunkdateien nicht finden konnte, die tatsächlich vorhanden waren.
- Es benötigte einige trickreiche Konfigurationen, um mit unserem Monorepo-Setup zu funktionieren.
- Es unterstützt MDX 3 nicht nativ, daher mussten wir einen benutzerdefinierten Transformer dafür erstellen.
- Es unterstützt keine manuellen Chunks (zum Zeitpunkt des Schreibens befindet sich das manuelle Chunks-Feature noch im experimentellen Stadium), was in den meisten Fällen in Ordnung ist, aber manchmal braucht man es.
Warum haben wir uns also entschieden, zu etwas anderem zu migrieren?
- Wir waren mit Parcel 2.9.3 festgefahren, das im Juni 2023 veröffentlicht wurde. Jedes Mal, wenn eine neue Version danach veröffentlicht wurde, versuchten wir, ein Upgrade durchzuführen, aber es scheiterte immer mit Fehlern beim Build-Prozess.
- Die neueste Version von Parcel war 2.12.0, veröffentlicht im Februar 2024. Obwohl es fast täglich Commits gibt, wurde seitdem keine neue Version veröffentlicht.
Jemand hat sogar eine Diskussion eröffnet und gefragt ob Parcel tot ist. Die offizielle Antwort ist nein, Parcel ist immer noch am Leben, aber es befindet sich in einem Zustand von "wir arbeiten an einer großen Umstrukturierung und haben keine Zeit für kleinere Releases". Für uns ist es wie ein "Tod auf Raten": Die neueste Version, die wir verwenden können, ist mehr als ein Jahr alt, und wir wissen nicht, wann die nächste Version veröffentlicht wird. Es sieht tot aus, es verhält sich wie tot, also ist es für uns tot.
Warum Vite?
Wir kannten Vite durch Vitest. Vor einigen Monaten waren wir die ESM-Unterstützung von Jest (im Testen) leid und wollten etwas Neues ausprobieren. Vitest eroberte unser Herz mit der nativen ESM-Unterstützung und der Jest-Kompatibilität. Es bietet eine erstaunliche Entwicklererfahrung und wird von Vite angetrieben.
Der Status Quo
Du hast möglicherweise unterschiedliche Einstellungen in deinem Projekt, aber normalerweise findest du Plugin-Ersatz, da das Vite-Ökosystem floriert. Hier sind unsere Setups zum Zeitpunkt der Migration:
- Monorepo: Wir verwenden PNPM (v9) Workspaces, um unser Monorepo zu verwalten.
- Modul: Wir verwenden ESM-Module für all unsere Projekte.
- TypeScript: Wir verwenden TypeScript (v5.5.3) für all unsere Projekte mit Pfad-Aliassen.
- React: Wir verwenden React (v18.3.1) für all unsere Frontend-Projekte.
- Styling: Wir verwenden SASS-Module für das Styling.
- SVG: Wir verwenden SVGs als React-Komponenten.
- MDX: Wir haben MDX-Unterstützung mit GitHub Flavored Markdown und Mermaid.
- Lazy Loading: Wir müssen einige unserer Seiten und KomponentenLazy laden.
- Komprimierung: Wir erzeugen komprimierte Assets (gzip und brotli) für unsere Produktions-Builds.
Die Migration
Wir begannen die Migration, indem wir ein neues Vite-Projekt erstellten und damit herumspielten, um zu sehen, wie es funktioniert. Der Prozess war reibungslos und die tatsächliche Migration dauerte nur wenige Tage.
Out-of-the-box-Unterstützung
Vite bietet Out-of-the-box-Unterstützung für Monorepo, ESM, TypeScript, React und SASS. Wir mussten nur die notwendigen Plugins und Konfigurationen installieren, damit es funktioniert.
Pfad-Alias
Vite hat integrierte Unterstützung für Pfad-Aliase, zum Beispiel in unserem tsconfig.json
:
Wir mussten nur die gleiche Auflösung in unserer vite.config.ts
hinzufügen:
Beachte, dass der Ersatzpfad ein absoluter Pfad sein sollte, während er relativ zum Projekt-Root ist. Alternativ kannst du das vite-tsconfig-paths Plugin verwenden, um die Pfad-Aliase aus dem tsconfig.json
zu lesen.
React Fast Refresh und HMR
Obwohl Vite integrierte Unterstützung für HMR hat, ist es notwendig, ein Plugin zu installieren, um React Fast Refresh zu aktivieren. Wir verwendeten das @vitejs/plugin-react Plugin, das vom Vite-Team zur Verfügung gestellt wird und großartige Unterstützung für React-Funktionen wie Fast Refresh bietet:
SVG als React-Komponente
Wir verwenden das vite-plugin-svgr Plugin, um SVGs in React-Komponenten zu konvertieren. Es ist so einfach wie das Hinzufügen des Plugins zur Vite-Konfiguration:
Wir haben jedoch nicht angegeben, unter welchen Umständen die SVGs in React-Komponenten konvertiert werden sollten, sodass alle Importe konvertiert wurden. Das Plugin bietet eine bessere Standardkonfiguration: Nur die SVGs, die mit der Erweiterung .svg?react
importiert werden, werden konvertiert. Wir haben unsere Importe entsprechend aktualisiert.
SASS-Module
Obwohl Vite integrierte Unterstützung für SASS-Module hat, gibt es eine Sache, auf die wir achten müssen: wie die Klassennamen formatiert werden. Es könnte für Benutzer und unsere Integrationstests problematisch sein, wenn die Klassennamen nicht konsistent formatiert sind. Die Ein-Zeilen-Konfiguration in der vite.config.ts
kann das Problem lösen:
Übrigens haben Parcel und Vite unterschiedliche Geschmackssorten beim Importieren von SASS-Dateien:
Die * as
-Syntax funktioniert jedoch in Vite, führt jedoch dazu, dass die Modularizierung der Klassennamen verloren geht, wenn du dynamische Schlüssel verwendest, um auf das styles
-Objekt zuzugreifen. Beispielsweise:
MDX-Unterstützung
Da Vite unter der Haube Rollup verwendet, können wir das offizielle @mdx-js/rollup Plugin verwenden, um MDX sowie dessen Plugins zu unterstützen. Die Konfiguration sieht wie folgt aus:
Das remarkGfm
-Plugin wird verwendet, um GitHub Flavored Markdown zu unterstützen, und das rehypeMdxCodeProps
-Plugin wird verwendet, um die Props an die Code-Blöcke in den MDX-Dateien zu übergeben, ähnlich wie es Docusaurus tut.
Mermaid-Unterstützung innerhalb von MDX
Wir möchten Mermaid-Diagramme genauso wie andere Programmiersprachen in unseren MDX-Dateien verwenden. Die Verwendung sollte so einfach sein wie bei anderen Code-Blöcken:
Sollte gerendert werden als:
Da unsere App sowohl helle als auch dunkle Themen unterstützt, haben wir ein wenig programmiert, um die Mermaid-Diagramme mit dem Dunkelmodus kompatibel zu machen. Eine React-Komponente wurde erstellt:
useTheme
ist ein benutzerdefinierter Hook, um das aktuelle Thema aus dem Kontext zu erhalten. Die mermaid
-Bibliothek wird asynchron geladen, um die Ladegröße für den anfänglichen Seitenaufruf zu reduzieren.
Für den Codeblock in der MDX-Datei haben wir eine einheitliche Komponente, um die Aufgabe zu erledigen:
Schließlich definieren wir den MDX-Provider wie folgt:
Lazy Loading
Das ist keine Vite-spezifische Sache, aber es lohnt sich, es zu erwähnen, da wir während der Migration unsere Pages aktualisiert haben, um Lazy Loading zu nutzen und nichts danach kaputtging.
React hat eine eingebaute Funktion React.lazy
, um Komponenten Lazy zu laden. Es kann jedoch einige Probleme verursachen, wenn du schnell iterierst. Wir haben eine winzige Bibliothek namens react-safe-lazy entwickelt, um die Probleme zu lösen. Es ist ein Drop-In-Ersatz für React.lazy
und eine detaillierte Erklärung findest du in diesem Blogbeitrag.
Komprimierung
Es gibt ein nettes Plugin namens vite-plugin-compression, um komprimierte Assets zu erzeugen. Es unterstützt sowohl gzip- als auch brotli-Komprimierung. Die Konfiguration ist einfach:
Manuelle Chunks
Eine großartige Funktion von Vite (oder dem darunter liegenden Rollup) ist die Möglichkeit, manuelle Chunks zu erstellen. Während React.lazy
verwendet wird, um Komponenten Lazy zu laden, können wir mehr Kontrolle über die Chunks haben, indem wir die manuellen Chunks angeben, um zu entscheiden, welche Komponenten oder Module zusammen gebündelt werden sollen.
Zum Beispiel können wir zunächst den vite-bundle-visualizer verwenden, um die Bundle-Größe und Abhängigkeiten zu analysieren. Dann können wir eine geeignete Funktion schreiben, um die Chunks zu spalten:
Dev-Server
Im Gegensatz zum Produktions-Build wird Vite deinen Quellcode im Entwicklungsmodus NICHT bündeln (einschließlich verknüpfter Abhängigkeiten im selben Monorepo) und behandelt jedes Modul als Datei. Für uns lädt der Browser zum ersten Mal Hunderte von Modulen, was verrückt aussieht, aber in den meisten Fällen tatsächlich in Ordnung ist. Du kannst die Diskussion hier sehen.
Wenn es für dich ein Thema ist, ist eine alternative, aber nicht perfekte Lösung, die verknüpften Abhängigkeiten im optimizeDeps
-Feld der vite.config.ts
aufzulisten:
Dies wird die verknüpften Abhängigkeiten "vorbündeln" und den Entwicklungsserver schneller machen. Der Nachteil ist, dass HMR möglicherweise nicht wie erwartet für die verknüpften Abhängigkeiten funktioniert.
Zusätzlich verwenden wir einen Proxy, der die statischen Dateien in der Produktion bereitstellt und die Anfragen an den Vitest-Server in der Entwicklung weiterleitet. Wir haben einige spezifische Ports konfiguriert, um Konflikte zu vermeiden, und es ist auch einfach, dies in der vite.config.ts
einzurichten:
Umgebungsvariablen
Im Gegensatz zu Parcel verwendet Vite einen modernen Ansatz im Umgang mit Umgebungsvariablen durch die Verwendung von import.meta.env
. Es wird automatisch die .env
-Dateien laden und die Variablen im Code ersetzen. Es erfordert jedoch, dass alle Umgebungsvariablen mit VITE_
(konfigurierbar) beginnen.
Während wir Parcel verwendeten, ersetzte es einfach die process.env
-Variablen, ohne das Präfix zu überprüfen. Daher haben wir eine Workaround-Lösung entwickelt, indem wir das define
-Feld verwenden, um die Migration zu erleichtern:
Dies ermöglicht es uns, das Präfix allmählich zu den Umgebungsvariablen hinzuzufügen und das define
-Feld zu entfernen.
Fazit
Das war's! Wir haben erfolgreich unsere drei Frontend-Projekte von Parcel zu Vite migriert und hoffen, dass diese kurze Geschichte dir bei deiner Migration helfen kann. So sieht die Konfiguration am Ende aus: