Une image lourde coûte de la bande passante. Un kilo-octet de JavaScript coûte bien plus cher : il faut le télécharger, le décompresser, l’analyser, le compiler, puis l’exécuter — et tout cela se passe sur le fil principal, celui-là même qui doit répondre aux clics de l’utilisateur. C’est pourquoi, sur la page de la coopérative Niani, c’est le JavaScript de la carte de localisation qui plombe le score de réactivité, alors qu’on ne l’utilise presque jamais. Ce tutoriel vous apprend à mesurer, découper et alléger votre code avec Vite, pour qu’il ne pèse que ce qui est réellement nécessaire.
🎯 Ce que vous allez apprendre
- Comprendre pourquoi le JavaScript est l’octet le plus coûteux d’une page.
- Mesurer le poids de votre bundle et repérer les dépendances trop lourdes.
- Découper le code avec l’import dynamique pour ne charger que le nécessaire.
- Profiter du découpage automatique et de la minification de Vite.
- Charger les scripts sans bloquer et activer la compression du serveur.
🛠️ Ce que vous allez construire
Vous reprenez le code de la page Niani — un petit filtre de catalogue et une carte de localisation gourmande — et vous le réorganisez avec Vite. La carte ne sera plus chargée que lorsqu’un visiteur la demande, le reste du code sera découpé et minifié, et le poids JavaScript de la page d’accueil tombera bien en dessous de votre budget.
Prérequis
- Node.js 22 ou 24 LTS (Vite exige au minimum la 20.19 ; vérifiez avec
node -v). - Des bases en JavaScript moderne (modules,
import/export,async/await). - Un projet front-end, même minimal, à faire passer sous Vite.
- Niveau : intermédiaire.
- ⏱️ Temps estimé : ~50 minutes.
Étape 1 — Comprendre pourquoi le JavaScript coûte cher
Pour optimiser à bon escient, il faut saisir ce qui rend le JavaScript spécial. Un fichier image, une fois téléchargé, n’a plus qu’à être décodé et affiché. Un fichier JavaScript, lui, doit être analysé, compilé puis exécuté par le moteur du navigateur — un travail de calcul intense qui monopolise le fil principal.
Or c’est sur ce même fil principal que le navigateur traite les interactions de l’utilisateur. Tant qu’un gros script s’exécute, la page ne peut pas répondre aux clics : c’est exactement ce que mesurent le Total Blocking Time en laboratoire et l’INP sur le terrain. Réduire le JavaScript, ce n’est donc pas qu’une question de poids téléchargé : c’est rendre la page réactive.
La conséquence stratégique est simple : à poids égal, mieux vaut couper du JavaScript que n’importe quelle autre ressource. Et le meilleur JavaScript est celui qu’on ne charge pas — d’où l’idée centrale de ce tutoriel, ne livrer que le code réellement utilisé, au moment où il l’est.
✅ Point d’étape — Vous comprenez le lien entre poids du JavaScript, fil principal bloqué et mauvaise réactivité. C’est la justification de tout ce qui suit.
Étape 2 — Mesurer son bundle
On n’allège pas ce qu’on ne mesure pas. La première chose est de visualiser ce qui compose votre paquet JavaScript : quelles dépendances pèsent le plus, et lesquelles sont chargées alors qu’elles servent rarement. L’outil rollup-plugin-visualizer génère une carte interactive de votre bundle.
# Installer l'outil de visualisation
npm install -D rollup-plugin-visualizer
// vite.config.js
import { defineConfig } from 'vite';
import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig({
plugins: [
visualizer({ open: true, gzipSize: true })
]
});
Après un npm run build, un rapport s’ouvre et affiche chaque module proportionnellement à son poids. Sur Niani, ce graphique révèle l’évidence : la bibliothèque de cartographie représente à elle seule la majorité du bundle, alors que la carte est sous la ligne de flottaison et rarement consultée. C’est notre première cible.
✅ Point d’étape — Vous avez une carte visuelle de votre bundle et vous savez quelle dépendance pèse le plus. Notez-la : c’est la candidate au chargement à la demande.
Étape 3 — Mettre en place Vite
Vite est l’outil de build de référence pour le front-end moderne. En développement, il sert votre code via les modules natifs du navigateur, ce qui rend le rechargement quasi instantané. Pour la production, il délègue le regroupement à un bundler basé sur Rollup (remplacé par Rolldown, plus rapide, dans les versions récentes), avec deux atouts décisifs pour la performance : la minification et le découpage automatiques.
# Creer un projet Vite (au moment d'ecrire, Vite 8 ; la 7 fonctionne de meme)
npm create vite@latest niani-site
cd niani-site
npm install
npm run build
À la fin du build, regardez le dossier dist : Vite a minifié votre code (suppression des espaces, raccourcissement des noms) et l’a découpé en plusieurs fichiers, séparant notamment votre code applicatif des dépendances tierces. Ce découpage du « vendor » est précieux : quand vous modifiez votre code, le navigateur des visiteurs n’a pas à retélécharger les dépendances, qui restent en cache.
La minification à elle seule réduit souvent le poids d’un tiers, et elle est active par défaut — vous n’avez rien à configurer. C’est le socle sur lequel s’appuient les optimisations suivantes.
✅ Point d’étape — Votre projet se construit avec Vite, le code est minifié et découpé dans
dist. Comparez le poids au code non minifié de départ.
Étape 4 — Charger à la demande avec l’import dynamique
Voici le levier le plus puissant : ne charger une portion de code que lorsqu’elle devient nécessaire. C’est le rôle de l’import dynamique, import(), qui retourne une promesse et télécharge le module à l’exécution plutôt qu’au chargement de la page.
Appliquons-le à la carte de Niani. Au lieu de l’inclure dans le bundle principal, on ne la charge que lorsque l’utilisateur clique sur « Voir la carte ».
// La carte n'est PAS importee en haut du fichier.
// Elle est chargee seulement au clic.
const bouton = document.querySelector('#voir-carte');
bouton.addEventListener('click', async () => {
bouton.textContent = 'Chargement...';
const { afficherCarte } = await import('./carte.js');
afficherCarte(document.querySelector('#conteneur-carte'));
});
Vite détecte ce import() et place automatiquement carte.js et sa lourde dépendance dans un fichier séparé, exclu du bundle initial. Résultat : la page d’accueil ne télécharge plus la bibliothèque de cartographie au démarrage. Les visiteurs qui ne cliquent jamais sur la carte — la grande majorité — ne paient jamais son poids. Ceux qui cliquent attendent une fraction de seconde, ce qui est parfaitement acceptable pour une action volontaire.
La même technique s’applique à tout ce qui est lourd et non immédiat : un éditeur de texte riche, un graphique, un sélecteur de date élaboré, une fenêtre modale complexe.
✅ Point d’étape — Votre dépendance lourde est dans un fichier à part, chargé au clic. Dans l’onglet Réseau, elle n’apparaît qu’après l’interaction.
Étape 5 — Élaguer le code mort et les dépendances lourdes
Au-delà du découpage, on réduit le bundle en supprimant ce qui ne sert pas. Deux techniques se combinent. La première est le tree-shaking : Vite, grâce aux modules ES, élimine automatiquement le code exporté mais jamais importé. Encore faut-il l’aider en important précisément ce dont on a besoin, plutôt qu’une bibliothèque entière.
// A eviter : importe toute la bibliotheque
import _ from 'lodash';
const noms = _.map(produits, 'nom');
// A preferer : importe seulement la fonction utile
import map from 'lodash-es/map';
const noms = map(produits, 'nom');
La seconde technique est le remplacement des dépendances lourdes par des alternatives légères, voire par du code natif. Une bibliothèque de manipulation de dates massive peut souvent céder la place à une option plus petite ou aux fonctions natives du navigateur (l’objet Intl formate les dates sans aucune dépendance). Chaque dépendance retirée, c’est du poids et du temps d’exécution en moins.
Le rapport de visualisation de l’étape 2 est votre guide : il pointe les grosses dépendances une par une. Pour chacune, posez la question : en ai-je vraiment besoin, en entier, dès le chargement ?
✅ Point d’étape — Vous importez des fonctions ciblées plutôt que des bibliothèques entières, et vous avez remplacé au moins une dépendance lourde. Le bundle a encore minci.
Étape 6 — Charger sans bloquer et compresser
Reste à livrer ce code optimisé de la bonne façon. Les scripts de type module générés par Vite sont différés par nature : ils ne bloquent pas l’affichage et s’exécutent après l’analyse du document. Vous n’avez donc pas à ajouter defer manuellement, mais le principe reste : aucun script bloquant dans l’en-tête.
<!-- Genere par Vite : module, donc differe automatiquement -->
<script type="module" src="/assets/index.js"></script>
Pour les modules critiques que le navigateur va de toute façon demander, Vite ajoute des indices modulepreload afin de les télécharger plus tôt, en parallèle. Le dernier levier se joue côté serveur : activer la compression. Servir les fichiers JavaScript en Brotli (ou à défaut en gzip) réduit encore leur taille sur le réseau de 70 à 80 %. La plupart des hébergeurs et des CDN l’activent en une option ; vérifiez dans l’onglet Réseau que vos fichiers arrivent avec un en-tête content-encoding: br.
✅ Point d’étape — Vos scripts sont des modules différés et arrivent compressés en Brotli. L’en-tête
content-encodingle confirme dans l’onglet Réseau.
Étape 7 — Vérification finale : tenir un budget JavaScript
Fixez-vous un budget — par exemple, moins de 170 kilo-octets de JavaScript compressé au chargement de la page d’accueil — et vérifiez-le après le build. Relancez Lighthouse : le Total Blocking Time doit avoir chuté, et avec lui le risque de mauvais INP sur le terrain. Le rapport de visualisation, lui, doit montrer un bundle initial épuré, la carte et les autres modules lourds relégués dans des fichiers chargés à la demande.
La page Niani est désormais légère au démarrage : seul le code nécessaire à l’affichage et au filtre est livré, le reste arrive à la demande, le tout minifié et compressé. Vous avez bouclé la série : mesurer, puis corriger l’affichage (LCP), la réactivité (INP), la stabilité (CLS), et enfin alléger les deux grandes sources de poids, les médias et le code. C’est l’ensemble de ces leviers que le guide principal articule en une démarche cohérente.
🐞 Pièges fréquents
| Symptôme | Cause probable | Correctif |
|---|---|---|
| L’import dynamique ne réduit pas le bundle initial | Le module est aussi importé statiquement ailleurs | Vérifier qu’aucun import en haut de fichier ne charge déjà ce module |
| Une bibliothèque entière reste dans le bundle malgré le tree-shaking | Import par défaut d’un paquet non modulaire (CommonJS) | Utiliser la variante ES du paquet et importer des fonctions ciblées |
| Les fichiers ne sont pas compressés | Compression non activée côté serveur ou CDN | Activer Brotli/gzip ; vérifier content-encoding dans l’onglet Réseau |
| Erreur « process is not defined » au build | Code prévu pour Node exécuté côté navigateur | Déplacer ce code côté serveur ou utiliser les variables d’environnement Vite (import.meta.env) |
🌍 Réalités du terrain
Sur les appareils d’entrée de gamme répandus en Afrique de l’Ouest, le coût d’exécution du JavaScript est le facteur le plus pénalisant, bien plus que le simple temps de téléchargement. Un processeur lent met plusieurs fois plus de temps à analyser et exécuter le même bundle : un script qui s’exécute en 100 millisecondes sur votre machine peut en prendre 400 sur un téléphone courant à Niamey ou Cocody. Couper du JavaScript, c’est donc rendre la page utilisable, pas seulement plus rapide à charger.
L’import dynamique est ici votre meilleur allié, car il évite de faire payer à tous les visiteurs le coût d’une fonctionnalité minoritaire. Sur un catalogue marchand, livrer une page d’accueil légère et différer le reste, c’est s’assurer qu’un client sur un appareil modeste puisse parcourir les produits sans que l’interface ne se fige.
✅ Récapitulatif
Vous savez désormais maîtriser le poids et le coût du JavaScript : comprendre pourquoi il est l’octet le plus cher, mesurer votre bundle, le découper avec l’import dynamique, l’élaguer par tree-shaking et remplacement de dépendances, puis le livrer sans bloquer et compressé. Avec Vite, l’essentiel — minification, découpage, modules différés — est automatique ; votre travail consiste surtout à décider quoi charger et quand.
🧾 Aide-mémoire
| Élément | Rôle |
|---|---|
rollup-plugin-visualizer |
Cartographier le poids du bundle |
npm create vite@latest |
Démarrer un projet avec build optimisé |
import() dynamique |
Charger un module à la demande |
| Imports ciblés + variantes ES | Permettre le tree-shaking |
type="module" |
Script différé par nature, non bloquant |
| Brotli / gzip | Compresser les fichiers servis |
| Budget | p. ex. < 170 Ko de JS compressé au chargement |
💪 À vous de jouer
Repérez dans votre projet la dépendance la plus lourde qui n’est pas indispensable au premier affichage, et chargez-la avec un import() déclenché par une interaction. Comparez le poids du bundle initial avant et après.
Voir une piste de solution
Une bibliothèque de cartographie ou de graphiques pèse souvent plus que tout le reste du code applicatif réuni. En la sortant du bundle initial via import() au clic, la page d’accueil peut perdre 60 à 80 % de son poids JavaScript. Le Total Blocking Time chute en proportion, et l’INP sur le terrain suit dans les semaines qui suivent. C’est l’optimisation la plus rentable côté code.
Dans la même série
- Optimiser l’INP — moins de JavaScript, un fil principal plus réactif.
- Mesurer la performance web — suivre le Total Blocking Time et le poids du bundle.
Pour aller plus loin
- 🔝 Retour au guide principal : Core Web Vitals et performance web.
- Documentation officielle : Vite — Build de production et web.dev — découpage du code.
FAQ
Dois-je utiliser un framework pour optimiser mon JavaScript ?
Non. Vite fonctionne avec ou sans framework, y compris sur du JavaScript pur. Le découpage, le tree-shaking et l’import dynamique s’appliquent à tout projet à base de modules ES. Un framework apporte d’autres outils, mais n’est pas requis pour ces optimisations.
L’import dynamique ralentit-il l’expérience au clic ?
Très légèrement, le temps de télécharger le module — généralement une fraction de seconde. C’est un excellent compromis : on évite de faire payer ce coût à tous les visiteurs au chargement, pour ne le faire supporter qu’à ceux qui utilisent réellement la fonctionnalité, et au moment où ils la demandent.
Quelle différence entre minification et compression ?
La minification réécrit le code pour le raccourcir (espaces, noms de variables) au moment du build. La compression (Brotli, gzip) réduit la taille des fichiers à la volée pendant le transfert réseau, côté serveur. Les deux se cumulent et sont complémentaires.