Aller au contenu principal

Passer de react-scripts à viteJS

· 7 minutes de lecture
Damien Buchet

Si vous travaillez sur des "grosses" applications (créées avec create-react-app), vous vous êtes surement rendu compte que les temps de démarrage et de "hot-refresh" de votre serveur de développement prennent de plus en plus de temps au fur et à mesure que l'application grossissait.

Sur Odyssey, la plateforme pédagogique de la Wild Code School, ces temps de chargement sont devenus de vraies contraintes pour l'équipe. Et pour peu que vous soyez comme moi accro au CTRL+S toutes les 3 frappes clavier, cela pouvait ruiner complètement votre expérience de développement. Et donc générer de la frustration.

Alors comment retrouver le plaisir de développer avec React sur une grosse application ? ViteJS ! Et le tout, sans jamais avoir à éjecter react-scripts, et reversible à tout moment ! Alors, comment effectuer la migration ? Ca passe par ici 👇

Comparaison

Mais tout d'abord, comparons quelques chiffres.

react-scriptsviteJS
yarn start initial4 minutes 30 secondes1 minute 33 secondes
yarn start sans changement de dépendance4 minutes 30 secondes22 secondes
HMR8 secondes1 seconde

Quelques précisions pour ces mesures de temps. Tout d'abord l'application est toujours sur react-scripts@4. Je n'ai aucune idée si ces temps seraient meilleurs avec react-scripts@5 et webpack@5 mais notre tentative d'upgrade vers react-scripts@5 s'est soldée par un échec cuisant.

Et quelle est la différence entre les 2 premières lignes ? ViteJS va précharger vos dépendances lorsque vous lancez le serveur de développement pour la première fois. Lorsque vous relancez le serveur une autre fois, si vos dépendances n'ont pas changées, viteJS va utiliser les dépendances déjà préchargées. Si vous changez vos dépendances, viteJS repassera par une phase de préchargement la première fois que vous relancez le serveur de développement.

Donc nous voyons ici que nous ne parlons pas d'optimisation de temps de chargement pour gagner 12 millisecondes. On parle de -7 secondes (+800%) sur le HMR et de 4 minutes (1200%) sur le temps de démarrage du serveur de développement.

La migration

Premier point évidemment, ajouter vitejs en tant de devDependency

yarn add vite -D

Il nous faudra ensuite créer un fichier vite.config.js à la racine de notre projet. Ce fichier va embarquer toute la configuration de vite et pour l'instant, il ne contient que ca :

import { defineConfig } from 'vite';

export default defineConfig({});

Il faut ensuite rajouter quelques configurations :

  • Activer le fast-refresh
  • Utiliser le runtime JSX
  • Utiliser quelques presets Babel propres à React
  • Injecter automatiquement import React dans les composants

Heureusement, un plugin configure toutes ses étapes pour nous @vitejs/plugin-react Il nous suffit de l'installer en devDependency et de l'ajouter ensuite dans notre fichier de configuration

yarn add @vitejs/plugin-react -D
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
plugins: [
react()
],
});
Attention !

Seule étape un peu pénible. viteJS n'injecte import React que dans les fichiers .jsx ou .tsx. Ce qui veut dire que tous vos fichiers qui utilisent du JSX doivent avoir l'extension .jsx ou .tsx.

Si votre projet est correctement découpé / ordonné, cela ne devrait néanmoins par être trop long de renommer tous les composants 😉

Il faudra ensuite modifier votre fichier public/index.html. Tout d'abord il faut que ce fichier index.html soit à la racine de votre projet, et non dans le dossier public/

Vous devrez ensuite effectuer quelques modifications à l'interieur :

  • %PUBLIC_URL% n'est pas requis par vite. Vous pouvez simplement le supprimer. vite ira par défaut toujours chercher les assets dans le dossier public/
- <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
+ <link rel="icon" href="/favicon.ico" />
  • Là où react-scripts s'injecte tout seul dans ce fichier, on aura pour vite besoin de créer une balise script pour charger notre script
  <div id="root"></div>
+ <script type="module" src="/src/index.jsx"></script>

On va ensuite modifier le script start de notre package.json pour démarrer le serveur de développement avec vite

- "start": "react-scripts start",
+ "start": "vite",

Et voilà ! Vous pouvez démarrer le serveur avec un yarn start et profiter des performances de vite !

Pour aller plus loin

Vous aurez peut-être besoin de configuration supplémentaires suivant les fonctionnalités que vous utilisez. En voici quelques une que nous utilisons dans notre application

process.env

vite n'utilise pas process.env. Donc si vous l'utilisez dans vos composants, notamment pour charger des images de votre dossier public. Vous pouvez au choix supprimer process.env.PUBLIC_URL ou définir un default dans vite.config.js

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';

export default defineConfig({
plugins: [
react()
],
define: {
'process.env': {}
},
});

SVG

Si vous souhaitez utiliser des images SVG en utilisant

import { ReactComponent as Icon } from '[...]assets/icon.svg';

Vous pouvez utiliser le plugin vite-plugin-svgr qui vous permettra de ne faire aucun changement dans votre code. Il vous faudra donc installer le plugin, et le rajouter dans votre vite.config.js

yarn add vite-plugin-svgr -D
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';

export default defineConfig({
plugins: [
react(),
svgr(),
],
define: {
'process.env': {}
},
});

Import module en absolus (non-relatif)

Si vous avez configuré votre application pour importer des modules sans avoir des ../../../../ partout, vous pouvez également le faire avec vite, même si c'est un peu moins pratique qu'avec webpack et son baseUrl: 'src'. Vous pouvez ajouter des alias dans vite.config.js

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';
import path from 'path';

export default defineConfig({
plugins: [
react(),
svgr(),
],
define: {
'process.env': {}
},
resolve: {
alias: {
'hooks': path.resolve(__dirname, './src/hooks'),
'actions': path.resolve(__dirname, './src/actions'),
'api': path.resolve(__dirname, './src/api'),
'config': path.resolve(__dirname, './src/config'),
}
}
});

Bonus

Tout bête, mais un yarn start avec react-scripts ouvrait par défaut localhost:3000. Ce que vite ne fait pas. Encore une fois, une petite ligne de configuration pour retrouver ce comportement !

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';
import path from 'path';

export default defineConfig({
plugins: [
react(),
svgr(),
],
define: {
'process.env': {}
},
resolve: {
alias: {
'hooks': path.resolve(__dirname, './src/hooks'),
'actions': path.resolve(__dirname, './src/actions'),
'api': path.resolve(__dirname, './src/api'),
'config': path.resolve(__dirname, './src/config'),
}
},
server: {
open: '/'
},
});

Inconvénients

Alors y-a-t-il des inconvénients à utiliser vite plutôt que react-scripts ? Oui. Quelques-un quand même (outre le fait de devoir changer d'extension).

  • Le HMR de vite n'est pas capable (pour le moment ?) de garder le state interne des composants. Donc à chaque hot-reload, les states de vos composants seront réinitialisé. Ce n'est pas forcément problématique si vous avez un state manager type redux, mais ça peut être une contrainte.
  • Je vous ai parlé de développement mais quid du build ? Il est possible de builder votre projet en spécifiant le folder dans vite.config.js et en modifiant votre package.json
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import svgr from 'vite-plugin-svgr';
import path from 'path';

export default defineConfig({
plugins: [
react(),
svgr(),
],
define: {
'process.env': {}
},
resolve: {
alias: {
'hooks': path.resolve(__dirname, './src/hooks'),
'actions': path.resolve(__dirname, './src/actions'),
'api': path.resolve(__dirname, './src/api'),
'config': path.resolve(__dirname, './src/config'),
}
},
server: {
open: '/'
},
build: {
outDir: 'build',
},
});
  // package.json
- "build": "react-scripts build",
+ "build": "vite build",

Cependant, vite pour le build utilise rollup qui n'est compatible qu'avec des modules ESM (ou alors je n'ai pas trouvé comment facilement le configurer). Et il reste encore quand même pas mal de packages qui ne sont pas compatible avec ESM et qui donc soit échouent au build, soit génèrent une erreur au serve.

Aussi pour notre application, bien que nous utilisions vite pour le développement, nous sommes restés sur react-scripts pour le build. Cela implique d'avoir toujours notre fichier public/index.html (pour react-scripts) ainsi que notre fichier /index.html (pour vite). Nous avons donc 2 fichiers index.html, et c'est la seule contrainte que nous ayons pour faire tourner ces 2 environnements.