Accélérer vos pages Web : quelques réflexes ⚡️ (partie 3)
Tags: frontend, optimization, coding
Published: 2020-08-04 10:19:43

Cet article sera le troisième et dernier d'une série de trois :

  1. Images et polices personnalisées (https://dev.to/alexandrecantin/optimiser-vos-pages-web-quelques-reflexes-indispensables-g79)
  2. JavaScript et pré-chargement de ressources (https://dev.to/alexandrecantin/accelerer-vos-pages-web-quelques-reflexes-partie-2-2oh3)
  3. Contenus animés et LightHouse (cet article)

---

## 🎬 5 - Contenus animées : .mp4 et GIF
### 5.1 - Privilégier les vidéos .mp4 sur les GIF
Cette recommandation peut sembler contre-intuitive mais, dans le cas des GIFs, il est nécessaire de les convertir en .mp4 pour diminuer leur poids.

En effet, bien que le format GIF soit un format d'image, il s'agit d'une succession d'images et peut donc être assimilé à une vidéo. Toutefois, contrairement au format .mp4, le format GIF est moins bien optimisé.

Prenons par exemple, le GIF ci-dessous dont le poids est de 5.1 Mo :

![Chandler qui danse](https://dev-to-uploads.s3.amazonaws.com/i/g330m0b5hp3q6sqgqn10.gif)

Après une conversion en .mp4 (via [https://video.online-convert.com/fr/convertir/gif-en-mp4](https://video.online-convert.com/fr/convertir/gif-en-mp4) par exemple), son poids se retrouve divisé par 5 pour atteindre 1.1 Mo.

Pour ensuite obtenir un rendu et comportement similaires à ceux d'une image GIF, il est nécessaire d'ajouter les attributs `autoplay loop muted playsinline` à la balise `video` :

````
<video autoplay loop muted playsinline>
   <source src="video.mp4" type="video/mp4" />
</video>
```

Si vous souhaitez aller plus loin, il est aussi possible d'adapter le rendu en fonction de la qualité de la connexion Internet de l'utilisateur avec `navigator.connection.effectiveType` pour, par exemple, renvoyer une image (optimisée bien entendu) à la place d'une vidéo : 

```javascript
if(navigator.connection.effectiveType === "4g")
  return <Video src="animate-background.mp4" />

return <img src="background.png" alt="" />
```

Malheureusement, cette fonctionnalité est pour le moment très mal supportée par les différents navigateurs :

![La support n'est pas top !](https://dev-to-uploads.s3.amazonaws.com/i/53lw03topkh1xzbs89hj.png)

### 5.2 - Attention au player Youtube 
Youtube fournit un moyen simple d'héberger mais aussi d'intégrer des vidéos dans vos pages via une _iframe_. Toutefois, cette dernière va télécharger tous un ensemble de fichiers pour un total de 584 Ko !

![Total des téléchargement de Youtube pour 584 Kilos](https://dev-to-uploads.s3.amazonaws.com/i/44t0snl0mzw9kx68m9lr.png)

Bien sûr, à ce temps de téléchargement s'additionne le temps de _parsing_, compilation et d'exécution du navigateur. Qui plus est, l'utilisateur paiera ce prix qu'il **visionne ou non** cette vidéo !
Comment éviter cela ? Deux solutions sont envisageables.

#### 5.2.1 - Le _lazy loading_
La première solution renvoie au chapitre relatif au _lazy loading_ des images : l'iframe la plus optimale reste celle qu'on ne télécharge pas. De même, les deux solutions évoquées sont aussi d'actualités, à savoir :

1 - Utiliser _lazysizes_ en remplaçant l'attribut `src` par `data-src`: 

```html
<iframe frameborder="0" allowfullscreen=""
data-src="//www.youtube.com/embed/ZfV-aYdU4uE">
</iframe>
```

2 - Utiliser l'attribut `loading="lazy"` (dont le support - ou son manque de support - reste identique aux images) :

```html
<iframe frameborder="0" allowfullscreen="" src="//www.youtube.com/embed/ZfV-aYdU4uE" loading="lazy"></iframe>
```

Toutefois, le problème n'est résolu qu'à moitié : dès que la vidéo apparaît à l'écran de l'utilisateur, nous récupérons l'ensemble des 584 Ko de JavaScript.


#### 5.2.2 - Un _faux_ lecteur vidéo

L'idée, au premier abord, peut sembler saugrenue mais permet d'obtenir le résultat escompté : remplacer le _player_ Youtube par un bouton contenant une image de pré-visualisation associée à une icône de lecture. En cas de clic, nous basculons ensuite sur l'iframe de Youtube en lecture automatique (via `autoplay=1`).

Note : pour réaliser ce composant, nous utiliserons React mais une transposition vers des équivalents Svelte, Vue ou Angular est facilement envisageable.

Ce composant accueillera deux paramètres que seront :
- l'identifiant de la vidéo Youtube
- le titre de vidéo (pour l'accessibilité - notamment ceux avec un lecteur d'écran)

Pour utiliser ce composant, nous procéderons de la sorte :
```javascript
<YoutubeVideo id="dQw4w9WgXcQ" title="Never gonna give you up" />
``` 

Tout d'abord, nous commencerons par créer le bouton en question :

```javascript
import React from "react";

function YoutubeVideo({ id, title }) {
  return (
    <button
      type="button"
      title={`Vidéo Youtube : ${title}`}
      style={buttonStyle}
      onClick={startVideo}
    >
      <img style={imgStyle} src={`https://img.youtube.com/vi/${id}/hqdefault.jpg`} alt="" />
      <img style={youtubeIconStyle} src="youtube-icon.svg" alt="" />
    </button>
  );
}

export default YoutubeVideo;
```

En ajoutant un peu de style à notre bouton, nous allons :
- indiquer les dimensions de la vidéo, dimensions qui seront identiques pour la vidéo et le bouton
- centrer l'icône de lecture verticalement et horizontalement
- ajuster l'image de pré-visualisation pour qu'elle occupe la totalité de l'espace disponible. D'ailleurs, concernant cette image, vous pouvez remarquer qu'elle provient directement de Youtube; l'image d'une vidéo pouvant être récupérée à l'adresse suivante : `https://img.youtube.com/vi/[IDENTIFIANT_VIDEO]/hqdefault.jpg`.


```css
/* Dimensions de la vidéo et du bouton */
const style = {
  width: "100%",
  height: "350px"
};

/* Centrer l'icône Youtube (entre autres...) */
const buttonStyle = {
  ...style,
  position: "relative",
  overflow: "hidden",
  cursor: "pointer",
  display: "flex",
  justifyContent: "center",
  alignItems: "center",
  border: "none"
};

/* Image de pré-visualisation pour occuper tout l'espace */
const imgStyle = {
  width: "100%",
  height: "100%",
  position: "absolute",
  zIndex: 1
};

/* Placer l'icône au-dessus de celle de pré-visualisation */
const youtubeIconStyle = {
  zIndex: 2
};
```

Pour le moment, nous obtenons un bouton qui ne fait simplement rien... mais qui ressemble fortement à la _preview_ d'une vidéo Youtube :

![On a l'impression d'avoir un lecture Youtube mais c'est juste un bouton](https://dev-to-uploads.s3.amazonaws.com/i/6cd7igy43cgwfzs8lbx7.png)


La suite consistera à ajouter une variable au sein de notre composant, nommée `showVideo`, gérant l'affichage de l'_iframe_. Elle sera à `false` par défaut et signifiera que l'on affichera le bouton de pré-visualisation. Au clic sur le bouton, `showVideo` deviendra `true` et occasionnera le démarrage de la vidéo.

```javascript
...
function YoutubeVideo({ id, imgSrc, title }) {
  // On utilise les hooks React
  const [showVideo, setShowVideo] = useState(false);

  function startVideo() {
    setShowVideo(true);
  }

  ...

  return (
    <button
      onClick={startVideo} // <=== Action au clic
      type="button"
      title={`Vidéo Youtube : ${title}`}
      style={buttonStyle}
    >
      <img style={imgStyle} src={`https://img.youtube.com/vi/${id}/hqdefault.jpg`} alt="" />
      <img style={youtubeIconStyle} src="youtube-icon.svg" alt="" />
    </button>
  );
}
```

La dernière étape consistera à renvoyer l'iframe Youtube si `showVideo` a une valeur égale à `true`. Noter l'ajout de `autoplay=1` après l'URL de la vidéo Youtube, c'est ce paramètre qui permet de démarrer la vidéo sans nécessiter de second clic par l'utilisateur :

```javascript
  if (showVideo) {
    return (
      <div>
        <iframe
          sandbox="allow-scripts allow-same-origin allow-presentation"
          title={`Vidéo Youtube : ${title}`}
          src={`https://www.youtube.com/embed/${id}?autoplay=1&rel=0`}
          frameBorder="0"
          allow="autoplay; encrypted-media"
          allowFullScreen
          style={style}
        />
      </div>
    );
  }
```

Et c'est aussi simple que cela, nous venons de créer un composant n'affichant (et téléchargeant) le lecteur Youtube uniquement si cela est nécessaire.

![GIF présentation le rendu](https://dev-to-uploads.s3.amazonaws.com/i/jz58n4aslrp66jm08ms8.gif)

_J'aurai bien voulu convertir ce GIF en mp4 mais dev.to n'accepte pas les vidéos 😔_

Au final, voici le code de notre composant au complet :

```javascript
import React, { useState } from "react";

const style = {
  width: "100%",
  height: "350px"
};

const buttonStyle = {
  ...style,
  position: "relative",
  overflow: "hidden",
  cursor: "pointer",
  display: "flex",
  justifyContent: "center",
  alignItems: "center",
  border: "none"
};

const imgStyle = {
  width: "100%",
  height: "100%",
  position: "absolute",
  zIndex: 1
};

const youtubeIconStyle = {
  zIndex: 2
};

function YoutubeVideo({ id, title }) {
  const [showVideo, setShowVideo] = useState(false);

  function startVideo() {
    setShowVideo(true);
  }

  if (showVideo) {
    return (
      <div>
        <iframe
          sandbox="allow-scripts allow-same-origin allow-presentation"
          title={`Vidéo Youtube : ${title}`}
          src={`https://www.youtube.com/embed/${id}?autoplay=1&rel=0`}
          frameBorder="0"
          allow="autoplay; encrypted-media"
          allowFullScreen
          style={style}
        />
      </div>
    );
  }

  return (
    <button
      type="button"
      title={`Vidéo Youtube : ${title}`}
      style={buttonStyle}
      onClick={startVideo}
    >
      <img style={imgStyle} src={`https://img.youtube.com/vi/${id}/hqdefault.jpg`} alt="" />
      <img style={youtubeIconStyle} src="youtube-icon.svg" alt="" />
    </button>
  );
}
export default YoutubeVideo;

```

Cette optimisation peut sembler s'apparenter à du bricolage mais le résultat est bien là : une diminution de plus de 500 Ko du poids de votre page et un téléchargement uniquement si l'utilisateur souhaite consulter la vidéo.


## 🧜🏻‍♀️ 6 - Lighthouse
Et s'il existait un outil testant l'ensemble de ces critères, voire plus ? Bonne nouvelle, cet outil existe et il se nomme Lighthouse !

Directement embarqué dans le navigateur Google Chrome ou disponible en ligne à l'adresse [https://developers.google.com/web/tools/lighthouse#psi](https://developers.google.com/web/tools/lighthouse), Lighthouse regroupe tout un panel de tests mesurant la vitesse - entre autres - de votre page.

![Page d'accueil de l'extension Lighthouse](https://dev-to-uploads.s3.amazonaws.com/i/uup90l9w1ys1um3zm1jh.png)

L'outil vous attribue ensuite un score sur 100 et des points d'améliorations.

![Exemple d'un score Lighthouse](https://dev-to-uploads.s3.amazonaws.com/i/c6hlkkj1lnoqy8u0uqgr.png)

Auditer et surveiller ces métriques régulièrement (une fois par mois par exemple) est un réflexe important pour vous assurer que votre site continue d'être performant au fur et à mesure de son évolution.


Je vous invite à tester votre site dès maintenant et, si vous ne l'avez jamais fait, des surprises vous attendent sûrement 😉 !

---

## 🚀 Conclusion

Nous voici arrivés à la fin de cette série d'articles consacrée à l'amélioration du chargement des pages Web. J'en profite pour vous remercier d'avoir pris le temps de la lire et j'espère qu'elle vous aura plu 🙂

Toutefois, ce billet n'a pas la prétention d'être exhaustif et d'autres optimisations aurait pu être abordées comme http2, les CDN... Je vous invite d'ailleurs à l'indiquer en commentaires 🙂.

Je vous remercie d'avoir pris le temps de lire cette série et j'espère que dorénavant, vous contribuerez à créer un web plus léger 😉.

---

## 🔰 Aller plus loin

### Vidéos et articles
Voici un ensemble de ressources pour explorer l'univers de l'optimisation des chargements :

- Fast load times : https://web.dev/fast/

- Lazy-loading d'images et _iframe_ : https://web.dev/native-lazy-loading/

- Code-splitting in React : https://reactjs.org/docs/code-splitting.html#route-based-code-splitting

- Web performance made easy (Google I/O '18) :
 https://www.youtube.com/watch?v=Mv-l3-tJgGk

- Speed at Scale: Web Performance Tips and Tricks from the Trenches (Google I/O ’19) : https://www.youtube.com/watch?v=YJGCZCaIZkQ

### Sur Twitter
- Houssein Djirdeh : https://twitter.com/hdjirdeh
- Addy Osmani : https://twitter.com/addyosmani
- Katie Hempenius : https://twitter.com/katiehempenius
- Paul Irish : https://twitter.com/paul_irish
- Lighthouse : https://twitter.com/____lighthouse