Éliminer le CLS en optimisant les images avec Vite
J'adore Vite, non seulement parce qu'il est rapide, mais aussi pour son incroyable système de plugins.
À la base, un plugin Vite vous permet d'éditer, à la volée, n'importe quel fichier importé, qu'il existe ou non, dans votre projet.
Une fois que vous avez compris cela, tout devient possible. Tout en tant que plugin Vite !
Depuis des années maintenant, je construis des sites web axés sur le contenu où celui-ci est écrit en Markdown. J'aime aussi utiliser des images pour illustrer mon contenu.
Cependant, les images sont délicates. Pour améliorer les performances, vous voulez les charger en différé (lazy loading), mais le lazy loading provoque inévitablement du Cumulative Layout Shift (CLS) si vous ne réservez pas d'espace. Pour réserver cet espace, le navigateur doit connaître à l'avance les dimensions de l'image. Mais vous ne voulez pas toujours les mêmes dimensions partout, donc pour chaque image vous avez besoin de ses dimensions afin que le navigateur puisse réserver la bonne quantité d'espace.
Au-delà de ça, les images peuvent rapidement devenir volumineuses, et les héberger dans votre dépôt n'est pas idéal. Vous pourriez donc vouloir les transférer vers un bucket dédié. Cela signifie qu'elles seront servies depuis un domaine différent, ce qui, combiné au chargement différé, même avec les bonnes dimensions, laissera un espace vide jusqu'à ce qu'elles soient entièrement chargées.
C'est un problème soluble, mais il nécessite beaucoup de travail manuel. Je déteste le travail manuel et répétitif qui peut être automatisé !
!NOTE Cet article est dédié au contenu HTML. Un autre article couvre la gestion du contenu Markdown en utilisant Markdown Exit.
Vite à la rescousse
La première question qui vient à l'esprit est : Comment savoir si un plugin Vite est le bon outil pour le travail ?
C'est une question importante. Je pourrais simplement vous donner la solution, mais vous ne seriez pas en mesure d'appliquer une approche similaire à vos propres problèmes. Malheureusement, la plupart des tutoriels manquent cette étape.
Analysons le problème en regardant ses entrées et ses sorties.
L'entrée est probablement un extrait HTML brut comme celui-ci :
<div>
<img src="/path/to/image.jpg" alt="An image">
</div>
La sortie devrait être un extrait HTML optimisé comme celui-ci :
<div>
<img
src="https://cdn.example.com/path/to/image-optimized.jpg"
alt="An image"
width="600"
height="400"
loading="lazy"
style="background-image: url('data:image/svg+xml;base64,...'); background-size: cover;"
>
</div>
Vite est construit autour d'un pipeline qui traite les fichiers au fur et à mesure qu'ils sont importés. Cela signifie que nous pouvons nous greffer sur le pipeline, détecter quand le code contient une image, et le transformer en conséquence.
Notre cas d'utilisation correspond parfaitement aux capacités de Vite. Les plugins Vite peuvent avoir des effets de bord sur les fichiers importés. Ils débloquent des cas d'utilisation vraiment puissants comme la compression automatique d'images, le redimensionnement, ...
!NOTE Cet article suppose que les images sont disponibles localement. La gestion des images distantes ne sera pas couverte ici, mais je donnerai quelques pistes à la fin de l'article.
Construire le plugin
Quel est le plan ? C'est bien d'avoir une idée de ce que nous voulons accomplir, mais comment y parvenir ?
Nous devons nous greffer sur le pipeline de Vite et rechercher les templates Vue. Ensuite, nous devons trouver toutes les balises <img>, extraire leurs attributs src, charger les images, obtenir leurs dimensions, générer le placeholder flouté, et enfin remplacer les balises <img> originales par des versions optimisées.
!NOTE J'utilise Vue pour tous mes projets, mais la même approche peut être appliquée à n'importe quel framework frontend supporté par Vite.
Cela semble plus facile que ça ne l'est, surtout quand on ne sait pas par où commencer et quel outil utiliser.
Pour gérer tout le traitement d'image, j'utilise unpic. C'est un ensemble fantastique de primitives pour gérer tout ce qui concerne les images. Pour manipuler le code et générer des source maps, j'utilise MagicString.
C'est tout ce dont nous avons besoin !
Maintenant, nous pouvons créer un plugin Vite qui fait exactement ce que nous voulons.
!NOTE Vous pouvez utiliser un projet Vite + Vue comme point de départ. Le code source du plugin final est disponible sur GitHub.
Dans le fichier vite.config.ts de notre projet, créons d'abord un plugin :
import vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
vue(),
(() => {
return {
name: 'unpic',
}
})()
],
})
Au minimum, un plugin Vite doit avoir un nom. Notre plugin est une IIFE (Immediately Invoked Function Expression). Cela nous permet d'avoir un contexte pour stocker l'état sans dépendre de variables globales.
Ensuite, nous devons nous greffer sur la bonne partie du pipeline. Il existe beaucoup de hooks disponibles, mais en pratique, le plus utile est transform. Ce hook est appelé pour chaque fichier importé dans le projet. Il reçoit le code du fichier et son id (chemin). Nous voulons seulement traiter les fichiers Vue, donc nous pouvons filtrer par extension de fichier.
(() => {
return {
name: 'unpic',
async transform(code, id) {
if (!id.endsWith('.vue')) return
// Some magic will happen here
return {
code,
map: null,
}
}
}
})()
Mais nous pouvons le rendre encore plus performant. Appeler notre plugin sur chaque fichier est inutile, surtout avec Rolldown, où la communication entre Rust et JavaScript entraîne un petit surcoût. Pour éviter cela, nous pouvons utiliser des filtres pour ne traiter que les fichiers correspondant à certains critères. Dans notre cas, nous voulons seulement traiter les fichiers Vue.
(() => {
const imgTagRegex = /<img\s[^>]*src=["']([^"']+)["'][^>]*>/g
return {
name: 'unpic',
enforce: 'pre',
transform: {
filter: {
id: /\.vue$/, code: imgTagRegex, },
async handler(code) {},
}
}
})()
Ce filter garantit que notre plugin ne s'exécute que sur les fichiers Vue contenant au moins une balise <img>. Tellement mieux !
Merci de me lire ! Je m’appelle Estéban, et j’adore écrire sur le développement web et le parcours humain qui l’entoure.
Je code depuis plusieurs années et j’apprends encore de nouvelles choses chaque jour. Je partage ce que j’apprends, car j’aurais aimé disposer de ressources claires et complètes lorsque j’ai commencé la programmation.
Si vous avez une question ou souhaitez discuter, laissez un commentaire ci-dessous ou contactez-moi sur mes profils sociaux.
J’espère que cet article vous a appris quelque chose d’utile. Partagez-le, laissez un commentaire ou ajoutez une réaction si c’est le cas. Soutenir mon travail sur GitHub.
Suivez-moi
Estéban Soubiran
Ingénieur logiciel, conférencier et passionné d'open source.
Discussions
Chargement des discussions...
Ajouter un commentaire
Vérification de votre session...