De Parcel à Vite : Histoire courte d'une migration de 100K LOC
Nous avons migré nos trois projets frontend de Parcel à Vite, et le processus a été... fluide.
Le contexte
Nous avons trois principaux projets frontend chez Logto : l'expérience de connexion, la Console, et l'aperçu en direct. Ces projets sont tous en TypeScript, React, et modules SASS ; au total, ils comptent environ 100 000 lignes de code.
Nous aimions Parcel pour sa simplicité et son installation sans configuration. Je me souviens encore du jour où j'ai été choqué par la facilité avec laquelle il était possible de configurer un nouveau projet avec Parcel. Il suffit de lancer parcel index.html
et voilà, toutes les dépendances nécessaires sont installées et le projet fonctionne. Si tu es un développeur "expérimenté", tu peux ressentir la même chose en le comparant aux anciens jours de la configuration avec Gulp et Webpack. Parcel est comme une baguette magique.
La simplicité de Parcel était la principale raison pour laquelle nous l'avons utilisé si longtemps, même s'il pouvait être capricieux parfois. Par exemple :
- Parcel échouait parfois à empaqueter le projet car il ne pouvait pas trouver certains fichiers de chunk qui étaient en réalité présents.
- Il nécessitait des configurations un peu bricolées pour fonctionner dans notre configuration monorepo.
- Il ne supporte pas nativement MDX 3, donc nous avons dû créer un transformateur personnalisé.
- Il ne supporte pas les chunks manuels (au moment de l'écriture, la fonctionnalité des chunks manuels est encore en phase expérimentale), ce qui convient à la plupart des situations, mais parfois, tu en as besoin.
Alors pourquoi avons-nous décidé de migrer vers autre chose ?
- Nous étions coincés avec Parcel 2.9.3, qui a été publié en juin 2023. Chaque fois qu'une nouvelle version était publiée après cela, nous avons essayé de mettre à jour, mais cela échouait toujours avec des erreurs de build.
- La dernière version de Parcel était 2.12.0, publiée en février 2024. Bien qu'il y ait des commits presque quotidiens, aucune nouvelle version n'a été publiée depuis lors.
Quelqu'un a même ouvert une discussion pour demander si Parcel est mort. La réponse officielle est non, Parcel est toujours vivant, mais il est dans un état de "nous travaillons sur une grande refonte et n'avons pas le temps pour des versions mineures". Pour nous, c'est comme une "mort discrète" : la dernière version que nous pouvons utiliser date de plus d'un an et nous ne savons pas quand la prochaine version sera publiée. Ça ressemble à la mort, ça agit comme la mort, donc c'est mort pour nous.
Pourquoi Vite ?
Nous connaissions Vite grâce à Vitest. Il y a quelques mois, nous étions fatigués du support ESM de Jest (en test) et voulions essayer quelque chose de nouveau. Vitest a conquis nos cœurs avec le support natif des ESM et la compatibilité avec Jest. Il offre une expérience de développement incroyable et est propulsé par Vite.
Le status quo
Tu peux avoir des configurations différentes dans ton projet, mais généralement tu trouveras des remplacements de plugins à mesure que l'écosystème Vite fleurit. Voici nos configurations au moment de la migration :
- Monorepo : Nous utilisons les workspaces PNPM (v9) pour gérer notre monorepo.
- Module : Nous utilisons des modules ESM pour tous nos projets.
- TypeScript : Nous utilisons TypeScript (v5.5.3) pour tous nos projets avec des alias de chemins.
- React : Nous utilisons React (v18.3.1) pour tous nos projets frontend.
- Styling : Nous utilisons des modules SASS pour le style.
- SVG : Nous utilisons les SVG comme composants React.
- MDX : Nous avons MDX avec le Flavored Markdown de GitHub et la prise en charge de Mermaid.
- Lazy loading : Nous avons besoin de charger paresseusement certaines de nos pages et composants.
- Compression : Nous produisons des assets compressés (gzip et brotli) pour nos builds de production.
La migration
Nous avons commencé la migration en créant un nouveau projet Vite et en jouant avec pour voir comment il fonctionne. Le processus a été fluide et la migration réelle n'a pris que quelques jours.
Prise en charge immédiate
Vite a une prise en charge immédiate pour les monorepos, ESM, TypeScript, React et SASS. Nous avons seulement eu besoin d'installer les plugins et configurations nécessaires pour le faire fonctionner.
Alias de chemin
Vite possède un support intégré pour les alias de chemin, par exemple, dans notre tsconfig.json
:
Nous avons seulement eu besoin d'ajouter la même résolution dans notre vite.config.ts
:
Remarque : Le chemin de remplacement doit être un chemin absolu, bien qu'il soit relatif à la racine du projet. Alternativement, tu peux utiliser le plugin vite-tsconfig-paths pour lire les alias de chemin depuis tsconfig.json
.
React Fast Refresh et HMR
Bien que Vite ait un support intégré pour le HMR, il est nécessaire d'installer un plugin pour activer React Fast Refresh. Nous avons utilisé le plugin @vitejs/plugin-react qui est fourni par l'équipe Vite et qui a un excellent support pour les fonctionnalités React comme Fast Refresh :
SVG en tant que composant React
Nous utilisons le plugin vite-plugin-svgr pour convertir les SVG en composants React. C'est aussi simple que d'ajouter le plugin à la configuration Vite :
Cependant, nous n'avons pas spécifié les conditions dans lesquelles les SVG devaient être convertis en composants React, donc toutes les importations ont été converties. Le plugin offre une meilleure configuration par défaut : ne convertir que les SVG importés avec l'extension .svg?react
. Nous avons mis à jour nos importations en conséquence.
Modules SASS
Bien que Vite prenne en charge les modules SASS par défaut, il y a une chose dont il faut s'occuper : comment les noms de classes sont formatés. Cela peut être problématique pour les utilisateurs et nos tests d'intégration si les noms de classes ne sont pas formatés de manière cohérente. La configuration d'une ligne dans le vite.config.ts
peut résoudre le problème :
By the way, Parcel et Vite ont des façons différentes d'importer les fichiers SASS :
La syntaxe * as
, cependant, fonctionne dans Vite, mais elle entraînera une perte des noms de classe modulaires lorsque tu utilises des clés dynamiques pour accéder à l'objet styles
. Par exemple :
Support de MDX
Puisque Vite utilise Rollup sous le capot, nous pouvons utiliser le plugin officiel @mdx-js/rollup pour prendre en charge MDX ainsi que ses plugins. La configuration ressemble à ceci :
Le plugin remarkGfm
est utilisé pour prendre en charge le Flavored Markdown de GitHub, et le plugin rehypeMdxCodeProps
est utilisé pour passer les props aux blocs de code dans les fichiers MDX comme le fait Docusaurus.
Support de Mermaid dans MDX
Nous aimerions utiliser des diagrammes Mermaid dans nos fichiers MDX comme les autres langages de programmation. L'utilisation doit être aussi simple que les autres blocs de code :
Devrait être rendu comme :
Puisque notre application prend en charge les thèmes clair et sombre, nous avons codé un peu pour faire fonctionner les diagrammes Mermaid avec le thème sombre. Un composant React est créé :
useTheme
est un hook personnalisé pour obtenir le thème actuel à partir du contexte. La bibliothèque mermaid
est importée de manière asynchrone pour réduire la taille de chargement pour le chargement initial de la page.
Pour le bloc de code dans le fichier MDX, nous avons un composant unifié pour faire le travail :
Enfin, nous définissons le fournisseur MDX comme suit :
Lazy loading
Ce n'est pas une particularité de Vite, mais ça vaut la peine d'être mentionné puisque nous avons mis à jour nos pages pour utiliser le lazy loading pendant la migration, et rien ne s'est cassé ensuite.
React dispose d'une fonction intégrée React.lazy
pour charger paresseusement les composants. Cependant, cela peut causer des problèmes lorsque tu itères rapidement. Nous avons conçu une petite bibliothèque appelée react-safe-lazy pour résoudre les problèmes. C'est un remplacement direct pour React.lazy
et une explication détaillée peut être trouvée dans cet article de blog.
Compression
Il y a un plugin sympa appelé vite-plugin-compression pour produire des assets compressés. Il prend en charge les compressions gzip et brotli. La configuration est simple :
Chunks manuels
Une excellente fonctionnalité de Vite (ou du Rollup sous-jacent) est les chunks manuels. Alors que React.lazy
est utilisé pour charger paresseusement les composants, nous pouvons avoir plus de contrôle sur les chunks en spécifiant les chunks manuels pour décider quels composants ou modules doivent être groupés ensemble.
Par exemple, nous pouvons d'abord utiliser vite-bundle-visualizer pour analyser la taille du bundle et les dépendances. Ensuite, nous pouvons écrire une fonction appropriée pour diviser les chunks :
Serveur de développement
Contrairement au build de production, Vite ne regroupera PAS ton code source en mode développement (y compris les dépendances liées dans le même monorepo) et traitera chaque module comme un fichier. Pour nous, le navigateur chargera des centaines de modules pour la première fois, ce qui semble fou, mais c'est en fait normal dans la plupart des cas. Tu peux voir la discussion ici.
Si c'est un problème pour toi, une solution alternative mais pas parfaite est de lister les dépendances liées dans l'option optimizeDeps
de vite.config.ts
:
Cela préregroupera les dépendances liées et rendra le serveur de développement plus rapide. Le problème est que le HMR peut ne pas fonctionner comme prévu pour les dépendances liées.
De plus, nous utilisons un proxy qui sert les fichiers statiques en production et proxy les requêtes vers le serveur Vitest en développement. Nous avons configuré des ports spécifiques pour éviter les conflits, et c'est aussi facile à configurer dans le vite.config.ts
:
Variables d'environnement
Contrairement à Parcel, Vite utilise une approche moderne pour gérer les variables d'environnement en utilisant import.meta.env
. Il chargera automatiquement les fichiers .env
et remplacera les variables dans le code. Cependant, cela nécessite que toutes les variables d'environnement soient préfixées avec VITE_
(configurable).
Lorsque nous utilisions Parcel, il suffisait de remplacer les variables process.env
sans vérifier le préfixe. Donc, nous avons trouvé une solution de contournement utilisant le champ define
pour faciliter la migration :
Cela nous permet de progressivement ajouter le préfixe aux variables d'environnement et de supprimer le champ define
.
Conclusion
Ça y est ! Nous avons réussi à migrer nos trois projets frontend de Parcel à Vite, et nous espérons que cette courte histoire pourra t'aider dans ta migration. Voici à quoi ressemble la configuration à la fin :