<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Alexandre CANTIN</title>
    <description>The latest articles on DEV Community by Alexandre CANTIN (@alexandrecantin).</description>
    <link>https://dev.to/alexandrecantin</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F374020%2Fbc0fdf18-d02b-4a50-983e-3b76c1bab0dc.png</url>
      <title>DEV Community: Alexandre CANTIN</title>
      <link>https://dev.to/alexandrecantin</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/alexandrecantin"/>
    <language>en</language>
    <item>
      <title>Accélérer vos pages Web : quelques réflexes ⚡️ (partie 3)</title>
      <dc:creator>Alexandre CANTIN</dc:creator>
      <pubDate>Tue, 04 Aug 2020 10:19:43 +0000</pubDate>
      <link>https://dev.to/alexandrecantin/accelerer-vos-pages-web-quelques-reflexes-partie-3-4dcc</link>
      <guid>https://dev.to/alexandrecantin/accelerer-vos-pages-web-quelques-reflexes-partie-3-4dcc</guid>
      <description>&lt;p&gt;Cet article sera le troisième et dernier d'une série de trois :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Images et polices personnalisées (&lt;a href="https://dev.to/alexandrecantin/optimiser-vos-pages-web-quelques-reflexes-indispensables-g79"&gt;https://dev.to/alexandrecantin/optimiser-vos-pages-web-quelques-reflexes-indispensables-g79&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;JavaScript et pré-chargement de ressources (&lt;a href="https://dev.to/alexandrecantin/accelerer-vos-pages-web-quelques-reflexes-partie-2-2oh3"&gt;https://dev.to/alexandrecantin/accelerer-vos-pages-web-quelques-reflexes-partie-2-2oh3&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Contenus animés et LightHouse (cet article)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🎬 5 - Contenus animées : .mp4 et GIF
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 - Privilégier les vidéos .mp4 sur les GIF
&lt;/h3&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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é.&lt;/p&gt;

&lt;p&gt;Prenons par exemple, le GIF ci-dessous dont le poids est de 5.1 Mo :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fg330m0b5hp3q6sqgqn10.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fg330m0b5hp3q6sqgqn10.gif" alt="Chandler qui danse"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Après une conversion en .mp4 (via &lt;a href="https://video.online-convert.com/fr/convertir/gif-en-mp4" rel="noopener noreferrer"&gt;https://video.online-convert.com/fr/convertir/gif-en-mp4&lt;/a&gt; par exemple), son poids se retrouve divisé par 5 pour atteindre 1.1 Mo.&lt;/p&gt;

&lt;p&gt;Pour ensuite obtenir un rendu et comportement similaires à ceux d'une image GIF, il est nécessaire d'ajouter les attributs &lt;code&gt;autoplay loop muted playsinline&lt;/code&gt; à la balise &lt;code&gt;video&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;video autoplay loop muted playsinline&amp;gt;
   &amp;lt;source src="video.mp4" type="video/mp4" /&amp;gt;
&amp;lt;/video&amp;gt;
```



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 &amp;lt;Video src="animate-background.mp4" /&amp;gt;

return &amp;lt;img src="background.png" alt="" /&amp;gt;
```



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
&amp;lt;iframe frameborder="0" allowfullscreen=""
data-src="//www.youtube.com/embed/ZfV-aYdU4uE"&amp;gt;
&amp;lt;/iframe&amp;gt;
```



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



```html
&amp;lt;iframe frameborder="0" allowfullscreen="" src="//www.youtube.com/embed/ZfV-aYdU4uE" loading="lazy"&amp;gt;&amp;lt;/iframe&amp;gt;
```



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
&amp;lt;YoutubeVideo id="dQw4w9WgXcQ" title="Never gonna give you up" /&amp;gt;
```



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



```javascript
import React from "react";

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

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



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



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

  return (
    &amp;lt;button
      type="button"
      title={`Vidéo Youtube : ${title}`}
      style={buttonStyle}
      onClick={startVideo}
    &amp;gt;
      &amp;lt;img style={imgStyle} src={`https://img.youtube.com/vi/${id}/hqdefault.jpg`} alt="" /&amp;gt;
      &amp;lt;img style={youtubeIconStyle} src="youtube-icon.svg" alt="" /&amp;gt;
    &amp;lt;/button&amp;gt;
  );
}
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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>frontend</category>
      <category>optimization</category>
      <category>coding</category>
    </item>
    <item>
      <title>Accélérer vos pages Web : quelques réflexes ⚡️ (partie 2)</title>
      <dc:creator>Alexandre CANTIN</dc:creator>
      <pubDate>Wed, 22 Jul 2020 06:42:56 +0000</pubDate>
      <link>https://dev.to/alexandrecantin/accelerer-vos-pages-web-quelques-reflexes-partie-2-2oh3</link>
      <guid>https://dev.to/alexandrecantin/accelerer-vos-pages-web-quelques-reflexes-partie-2-2oh3</guid>
      <description>&lt;p&gt;Cet article sera le deuxième d'une série de trois :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Images et polices personnalisées (&lt;a href="https://dev.to/alexandrecantin/optimiser-vos-pages-web-quelques-reflexes-indispensables-g79"&gt;lire l'article&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;JavaScript et pré-chargement de ressources (cet article)&lt;/li&gt;
&lt;li&gt;Contenus animés et LightHouse (à venir)&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🃏 3 - JavaScript
&lt;/h2&gt;

&lt;p&gt;L'utilisation du JavaScript a littéralement explosé ses dernières années avec l'avènement de Node.js et ses nombreuses librairies disponibles via NPM, auxquelles s'ajoutent les &lt;em&gt;Single Page Application&lt;/em&gt; : Angular, Vue et React.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 - Code splitting
&lt;/h3&gt;

&lt;p&gt;Le terme SPA (&lt;em&gt;Single Page Application&lt;/em&gt;) résume à lui tout seul son principe sous-jacent : l'ensemble de notre application est contenu dans une unique page - et par conséquent &lt;strong&gt;dans un seul fichier JavaScript&lt;/strong&gt;, dont la taille est forcément corrélée au nombre de fonctionnalités proposées par votre site.&lt;/p&gt;

&lt;p&gt;Bien entendu, au niveau visuel, l'utilisateur a l'impression d'avoir face à lui plusieurs pages grâce à un découpage entre différents composants et notamment à l'aide d'un &lt;em&gt;router&lt;/em&gt;. Dans l'univers de React, le &lt;em&gt;router&lt;/em&gt; le plus utilisé et connu est le &lt;a href="https://dev.toreact-router-dom"&gt;https://github.com/ReactTraining/react-router/&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BrowserRouter&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Switch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./routes/Home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./routes/About&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;exact&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/a-propos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Switch&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Router&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Note : des équivalents existent pour Svelte / Vue / Angular mais nous nous focaliserons sur React ici 🙂&lt;/p&gt;

&lt;p&gt;Dans cet exemple, notre &lt;em&gt;bundle&lt;/em&gt; final contiendra le code de la page d'accueil et de la page "À propos"; mais aussi toutes les autres pages qui seront présentes dans ce &lt;em&gt;router&lt;/em&gt;, pouvant se compter par dizaines.&lt;br&gt;
Toutefois, vos utilisateurs parcourent rarement l'ensemble de vos pages mais récupère pourtant tout le JavaScript pour le faire : une optimisation en perspective donc !&lt;/p&gt;

&lt;p&gt;Pour pallier ce problème, le code &lt;em&gt;splitting&lt;/em&gt; a vu le jour. Ce dernier permet de subdiviser notre &lt;em&gt;bundle&lt;/em&gt; en différentes sous-divisions pour n'envoyer aux utilisateurs que le strict nécessaire pour la page courante.&lt;/p&gt;

&lt;p&gt;Dans l'écosystème React, cette possibilité nous est offerte par la combinaison de deux fonctions internes :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;lazy&lt;/em&gt; : prenant en paramètre un import dynamique, qui sera ici notre composant : &lt;code&gt;const About = lazy(() =&amp;gt; import('./routes/About'));&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;Suspense&lt;/em&gt; : composant React dont l'objectif est d'attendre la résolution d'une promesse, l'import dynamique de &lt;em&gt;lazy&lt;/em&gt; dans notre cas. &lt;em&gt;Suspense&lt;/em&gt; accepte aussi un paramètre &lt;code&gt;fallback&lt;/code&gt; pour indiquer un texte ou un composant à exécuter en attendant la résolution de notre promesse :&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;Suspense&lt;/span&gt; &lt;span class="na"&gt;fallback=&lt;/span&gt;&lt;span class="s"&gt;{&amp;lt;div&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Chargement...&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;}&amp;gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/Suspense&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Au final, afin d'obtenir un code-splitting fonctionnel, nous devons adapter l'exemple précédent comme suit :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Suspense&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;lazy&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BrowserRouter&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Switch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./routes/Home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lazy&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./routes/About&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Suspense&lt;/span&gt; &lt;span class="nx"&gt;fallback&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Chargement&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&amp;gt;}&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Switch&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;exact&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/a-propos&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Switch&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Suspense&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Router&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Bien sûr, cela peut sembler inutile de réaliser du &lt;em&gt;code-splitting&lt;/em&gt; sur un site contenant deux pages 🙂 mais rassurez-vous, l'effet est démultiplié au fur à mesure que le nombre de pages augmente. &lt;/p&gt;

&lt;h3&gt;
  
  
  3.2 - Poids des librairies externes
&lt;/h3&gt;

&lt;h4&gt;
  
  
  3.2.1 - &lt;em&gt;Bundle Phobia&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;NPM a permis l'introduction de bon nombre de librairies que nous pouvons inclure et utiliser dans notre JavaScript côté &lt;em&gt;front&lt;/em&gt;. Toutefois, leur accumulation alourdit petit à petit le poids de nos fichiers et par conséquent ralentit le chargement de notre page. Surveiller le poids des librairies tierces est donc un réflexe nécessaire.&lt;/p&gt;

&lt;p&gt;Pour cela, je ne peux que vous conseiller le site &lt;a href="https://bundlephobia.com/"&gt;BundlePhobia&lt;/a&gt; dont le nom est éloquent : le site est à destination des développeurs craignant d'alourdir le poids de leur &lt;em&gt;bundle&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HjlaFuLl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5vj5sizhr8c0ti9dmz46.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HjlaFuLl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5vj5sizhr8c0ti9dmz46.png" alt="Page d'accueil de BundlePhobia"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;BundlePhobia vous met à disposition un formulaire de recherche vous permettant d'indiquer la librairie à analyser. Il vous renvoie ensuite son poids et une estimation de son temps de chargement :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QbOCu3TY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2edv1gawvlrse9twlnno.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QbOCu3TY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/2edv1gawvlrse9twlnno.png" alt="Statistiques de bundlePhobia"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;N'oubliez pas de vérifier si la librairie en question est aussi &lt;em&gt;tree-shakable&lt;/em&gt;. Une librairie &lt;em&gt;tree-shakable&lt;/em&gt; est une librairie dont les parties non utilisées seront retirées du &lt;em&gt;bundle&lt;/em&gt; final, occasionnant donc une réduction du poids de nos fichiers JavaScript. Par chance, BundlePhobia nous l'indique 🥳&lt;/p&gt;

&lt;p&gt;Pour finir, nous avons aussi une liste de librairies alternatives offrant des fonctionnalités similaires :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WxyIGvi3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qqflia70wbn3cjan6uqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WxyIGvi3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/qqflia70wbn3cjan6uqw.png" alt="Autres librairies possibles"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  3.2.2 - &lt;em&gt;Import Cost&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;Afin de surveiller le poids de votre librairie d'une manière plus régulière, une extension &lt;em&gt;Visual Studio Code&lt;/em&gt; nommée &lt;em&gt;Import Cost&lt;/em&gt; vous indique le poids des librairies que vous incorporez dans votre code :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dc6WspKX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/c7azw7hdg5ynr97bajth.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dc6WspKX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/c7azw7hdg5ynr97bajth.gif" alt="Exemple dans Visual Studio Code"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vous trouverez cette extension facilement dans le &lt;em&gt;MarketPlace&lt;/em&gt; de &lt;em&gt;Visual Studio Code&lt;/em&gt; :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nRkLhnH8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6qg70uhash6yun8fqnei.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nRkLhnH8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/6qg70uhash6yun8fqnei.png" alt="Import cost"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  💠  4 - Préchargement de ressources secondaires avec &lt;code&gt;&amp;lt;link rel="preload" /&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 - Fonctionnement de &lt;code&gt;preload&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Les premiers fichiers téléchargés dans nos pages sont souvent la porte d'entrée pour le téléchargement d'autres fichiers (polices, scripts complémentaires, images...), ceci est d'autant plus vrai depuis l'arrivée de librairies/&lt;em&gt;frameworks&lt;/em&gt; comme React, Vue, Angular ou encore Svelte.&lt;/p&gt;

&lt;p&gt;Ce fonctionnement ralentit considérablement notre page car plusieurs vagues de téléchargement sont alors nécessaires pour générer le rendu final.&lt;/p&gt;

&lt;p&gt;Toutefois, vu que nous connaissons les fichiers qui seront récupérés en deuxième ou troisième vague de téléchargement, ne pourrait-on pas indiquer au navigateur de le pré-télécharger afin de les avoir directement à disposition quand nous en aurons besoin ? C'est tout à fait possible avec &lt;code&gt;&amp;lt;link rel="preload" /&amp;gt;&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/fonts/custom-font.woff2"&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"font"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Dans cet exemple, nous indiquons à notre navigateur de pré-télécharger le fichier &lt;code&gt;/fonts/custom-font.woff2&lt;/code&gt; car nous en aurons besoin &lt;em&gt;pour le premier affichage de notre page&lt;/em&gt;. &lt;br&gt;
J'insiste lourdement sur le terme &lt;em&gt;pour le premier affichage de notre page&lt;/em&gt;. En effet, si vous indiquez des ressources dont vous ne faites aucun usage direct, vous obtenez alors l'effet inverse : vous consommez des ressources réseaux au détriment de fichiers plus prioritaires ! À utiliser avec intelligence et précision donc 🙂.&lt;/p&gt;

&lt;p&gt;D'ailleurs, Google Chrome nous indique si nous avons pré-téléchargé un fichier dont nous ne faisons pas usage dans trois secondes suivant la fin du chargement de la page (événement &lt;em&gt;onload&lt;/em&gt;) :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SbePkanJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/x1hc9j9c7i6tznwwb7pr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SbePkanJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/x1hc9j9c7i6tznwwb7pr.png" alt="Message d'avertissement de Chrome"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  4.2 - Spécifications
&lt;/h3&gt;

&lt;p&gt;Au niveau de sa composition, cette balise autorise les attributs :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;code&gt;rel&lt;/code&gt; : contenant "preload" pour indiquer qu'on souhaite un pré-chargement d'une ressource (d'autres valeurs sont possibles mais on reviendra dessus plus tard)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;href&lt;/code&gt; : indiquant le lien de la ressource à récupérer, qui peut être un lien interne ou externe (Google Font par exemple).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;as&lt;/code&gt; : pour indiquer le type de ressource : "font", "script", "style", "video", "image"... (liste complète disponible &lt;a href="https://fetch.spec.whatwg.org/#concept-request-destination"&gt;à cette adresse&lt;/a&gt;). L'ajout et la complétion de cet attribut a une très grande importance : elle permet au navigateur d'appliquer la bonne &lt;em&gt;Content Security Policy&lt;/em&gt;, d'utiliser les bons &lt;em&gt;header&lt;/em&gt; HTTP mais aussi de mieux identifier la ressource pour éviter sa double récupération.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;crossorigin&lt;/code&gt; (optionnel) : permettant de gérer les &lt;em&gt;Cross-origin resource sharing&lt;/em&gt; (CORS) et devant contenir obligatoirement "origin" pour les polices afin d'éviter un double téléchargement (même depuis &lt;a href="https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/"&gt;une origine équivalente&lt;/a&gt;). &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;type&lt;/code&gt; (optionnel) : pour indiquer le format du fichier téléchargé et permettre au navigateur d'empêcher le téléchargement si ce type n'est pas géré (ex: &lt;code&gt;.webp&lt;/code&gt; et Safari)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;media&lt;/code&gt; : pour ne déclencher le téléchargement que sous certaines conditions :
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"image"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"small-background.jpg"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(max-width: 800px)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"preload"&lt;/span&gt; &lt;span class="na"&gt;as=&lt;/span&gt;&lt;span class="s"&gt;"image"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"large-background.jpg"&lt;/span&gt; &lt;span class="na"&gt;media=&lt;/span&gt;&lt;span class="s"&gt;"(min-width: 801px)"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h3&gt;
  
  
  4.3 - Support navigateur
&lt;/h3&gt;

&lt;p&gt;Au niveau de son support, et aussi bizarre que cela puisse paraître, seul Firefox ne supporte pas nativement l'attribut &lt;code&gt;rel="preload"&lt;/code&gt; qui est actuellement au statut expérimental et doit être activé dans la configuration du navigateur. Espérons que cette situation sera rapidement corrigée 🙂.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vqGu356b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gu6irx7sqi42hthcsoeh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vqGu356b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/gu6irx7sqi42hthcsoeh.png" alt="rel=&amp;quot;preload&amp;quot; sur Firefox n'est qu'une fonctionnalité expérimental"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fin de la deuxième partie
&lt;/h2&gt;

&lt;p&gt;Nous voici arrivés à la fin de cette deuxième partie consacrée aux optimisations liées JavaScript et le pré-chargement de ressources.&lt;br&gt;
Dans le prochain et dernier épisode, nous finirons en beauté les contenus animées (vidéos/GIF) et Lighthouse.&lt;/p&gt;

&lt;p&gt;Restez connecté 🙂.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔰 Aller plus loin
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Vidéos et articles
&lt;/h3&gt;

&lt;p&gt;Voici un ensemble de ressources pour explorer l'univers de l'optimisation des chargements :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fast load times : &lt;a href="https://web.dev/fast/"&gt;https://web.dev/fast/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Preload, Prefetch And Priorities in Chrome : &lt;a href="https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf"&gt;https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Preload: What Is It Good For? : &lt;a href="https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/"&gt;https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Code-splitting in React : &lt;a href="https://reactjs.org/docs/code-splitting.html#route-based-code-splitting"&gt;https://reactjs.org/docs/code-splitting.html#route-based-code-splitting&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Web performance made easy (Google I/O '18) :&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=Mv-l3-tJgGk"&gt;https://www.youtube.com/watch?v=Mv-l3-tJgGk&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Speed at Scale: Web Performance Tips and Tricks from the Trenches (Google I/O ’19) : &lt;a href="https://www.youtube.com/watch?v=YJGCZCaIZkQ"&gt;https://www.youtube.com/watch?v=YJGCZCaIZkQ&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>optimization</category>
      <category>coding</category>
      <category>frontend</category>
    </item>
    <item>
      <title>Accélérer vos pages Web : quelques réflexes ⚡️ (partie 1)</title>
      <dc:creator>Alexandre CANTIN</dc:creator>
      <pubDate>Mon, 08 Jun 2020 19:46:16 +0000</pubDate>
      <link>https://dev.to/alexandrecantin/optimiser-vos-pages-web-quelques-reflexes-indispensables-g79</link>
      <guid>https://dev.to/alexandrecantin/optimiser-vos-pages-web-quelques-reflexes-indispensables-g79</guid>
      <description>&lt;p&gt;Cet article sera le premier d'une série de trois :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Images et polices personnalisées (cet article)&lt;/li&gt;
&lt;li&gt;JavaScript et pré-chargement de ressources (à venir)&lt;/li&gt;
&lt;li&gt;Contenus animés et LightHouse (à venir)&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;Depuis sa naissance en 1989, le Web n'a cessé d'offrir plus de possibilités aux développeurs afin de créer la meilleure expérience possible pour nos utilisateurs : images, vidéos, interfaces dynamiques avec JavaScript, polices personnalisées etc. &lt;br&gt;
Toutefois, cela n'est pas sans conséquence : le poids de nos pages a augmenté et il n'est pas rare qu'une page Web dépasse désormais un voire plusieurs mégaoctets.&lt;/p&gt;

&lt;p&gt;D'ailleurs, selon &lt;a href="https://httparchive.org/"&gt;Httparchive.org&lt;/a&gt;, en dix ans, malgré l'augmentation constante des débits Internet, la vitesse de chargement des pages est restée... la même. De plus, Google considère désormais les performances comme un facteur influençant son algorithme de &lt;em&gt;ranking&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Dans cet article, nous présenterons ainsi différentes méthodes pour alléger vos pages. Cette liste sera non exhaustive et nous nous concentrerons sur les principaux leviers parmi la centaine (voire les milliers!) envisageables. &lt;/p&gt;



&lt;p&gt;&lt;strong&gt;Note : les éléments présentés ci-dessous visent les utilisateurs arrivant &lt;em&gt;pour la première fois&lt;/em&gt; sur votre site. Par conséquent, les &lt;em&gt;services workers&lt;/em&gt; ne seront pas abordés.&lt;br&gt;
De même, je n'aborderai pas des techniques que je considère comme déjà acquises comme la minification du CSS et du JavaScript&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🏞 1 - Les images
&lt;/h2&gt;

&lt;p&gt;Omniprésentes, les images sont les principaux responsables de l'alourdissement du Web. Leur bonne gestion doit donc être l'une de vos priorités et j'oserai même dire un &lt;strong&gt;réflexe indispensable&lt;/strong&gt; en tant que développeur.&lt;/p&gt;
&lt;h3&gt;
  
  
  1.1 - Réduction du poids : PNG et JPG
&lt;/h3&gt;

&lt;p&gt;Peut-être l'ignorez-vous (ou n'y pensez simplement pas) mais il est possible d'optimiser les images pour en réduire -drastiquement- le poids.&lt;br&gt;
Pour ma part, je ne peux que vous conseillez d'utiliser &lt;a href="https://tinypng.com/"&gt;https://tinypng.com/&lt;/a&gt; qui fonctionne via un simple &lt;em&gt;drag and drop&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LHboPn6k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/emepr5gelf0vfwn448fj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LHboPn6k--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/emepr5gelf0vfwn448fj.png" alt="Image de la page d'accueil de TinyPNG"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nous allons maintenant tester son efficacité avec l'image de ce chat au format JPEG et pesant 357 Ko sans optimisation :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6S1tMs0b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3xkgccus5936km1z7l9y.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6S1tMs0b--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3xkgccus5936km1z7l9y.jpg" alt="Image d'un chat mignon de 357 Ko"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Si nous l'optimisons, TinyPng nous indique que le poids du fichier a été réduit de 49%, de 357 Ko à 181 Ko :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---99wX85i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cexa0xyqifylbuq9pur8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---99wX85i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/cexa0xyqifylbuq9pur8.png" alt="Fenêtre de rendu de TinyPng après conversion"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Un poids divisé de moitié pour un rendu équivalent :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--awG2cwq2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5f3l067ijz8rqc8hs34w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--awG2cwq2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5f3l067ijz8rqc8hs34w.jpg" alt="Le même chat... en plus léger"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sachant que ce gain se répète pour chacune image de votre page, on comprend vite l'intérêt crucial de bien optimiser l'ensemble de ses images.&lt;/p&gt;
&lt;h3&gt;
  
  
  1.2 - Quand choisir JPEG et PNG ?
&lt;/h3&gt;

&lt;p&gt;Un autre point important est de choisir le bon format en fonction de vos besoins. En effet, ils répondent tous les deux à des exigences différentes et votre choix dépendra de plusieurs facteurs :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;la taille de l'image : pour les "grandes" images (photos notamment), il faudra privilégier le format JPEG. Pour les "petites" images (émoticônes, icônes...), il faudra opter pour les PNG. Et si votre image est de taille "moyenne" ? Il faudra mener votre propre enquête pour déterminer le meilleur choix (en fonction du poids mais aussi des autres critères). &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;précision du dessin : le format JPEG, étant un format compressé, les contours perdent en netteté et cela n'est pas forcément souhaitable (graphiques, logo etc.). Il faudra opter pour le format PNG dans ce cas.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;transparence : dans ce cas-là, le PNG est la seule solution 🙂&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  1.3 - Réduction du poids : SVG
&lt;/h3&gt;

&lt;p&gt;Les images SVG peuvent aussi être optimisées. Pour ma part, j'utilise &lt;a href="https://jakearchibald.github.io/svgomg/"&gt;SVGOMG&lt;/a&gt; qui fonctionne lui aussi via &lt;em&gt;drag and drop&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4SMWHLle--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vjy0wpfrcji1hhxjrjxb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4SMWHLle--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vjy0wpfrcji1hhxjrjxb.png" alt="Interface de SVGOMG"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dans l'exemple ci-dessus, on constate que le poids de notre SVG a réduit de 60%, passant de 1.5 Ko à 643 bytes et, même si le résultat reste moins spectaculaire comparé aux PNG et JPG, cela reste toutefois appréciable 🙂&lt;/p&gt;
&lt;h3&gt;
  
  
  1.4 - Allez plus loin avec Webp
&lt;/h3&gt;

&lt;p&gt;Créé par Google en 2010, le Webp est un format d'image qui se veut - en moyenne -  30% plus léger comparé au JPEG et 25% par rapport au PNG; tout en conservant gardant les effets de transparence pour ce dernier format.&lt;/p&gt;

&lt;p&gt;Le Webp est donc un remplaçant de choix pour tous vos fichiers JPG et au niveau des PNG, Webp étant un format compressé, cela dépendra principalement de la précision du contour que vous souhaitez obtenir.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q5fFWtRR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3ohae6tfbugruibyaa6t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q5fFWtRR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3ohae6tfbugruibyaa6t.png" alt="Comparaison Webp et JPEG : un poids de fichier inférieur de 35%"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Un des soucis majeur de ce format est son support dans les principaux navigateurs. En effet, bien que Chrome, Firefox et Edge (uniquement dans sa dernière version) acceptent ce format, Safari le supportera dans sa prochaine version : &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---mwRvYjK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3divqbvqv05scjqk82by.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---mwRvYjK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3divqbvqv05scjqk82by.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cela ne doit pas pour autant vous décourager d'utiliser le format Webp et nous verrons plus tard comme gérer les navigateurs non compatibles.&lt;/p&gt;

&lt;p&gt;Pour convertir vos images en Webp, je peux vous conseiller d'utiliser &lt;a href="https://www.xnview.com/fr/xnconvert/"&gt;XnConvert&lt;/a&gt; qui fonctionne directement sur votre ordinateur (disponible pour Windows/Linux/MacOs). Comme solutions en ligne, &lt;a href="https://ezgif.com/png-to-webp"&gt;https://ezgif.com/png-to-webp&lt;/a&gt; et &lt;a href="https://ezgif.com/jpg-to-webp"&gt;https://ezgif.com/jpg-to-webp&lt;/a&gt; fonctionnent très bien 🙂.&lt;/p&gt;

&lt;p&gt;La conversion se réalise en 3 étapes (nous partirons de l'image du chat optimisée précédemment) :&lt;/p&gt;

&lt;p&gt;1- Ajouter les images :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--b0juLZLa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/08cvhlizcx9b1p27210x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--b0juLZLa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/08cvhlizcx9b1p27210x.png" alt="Etape 1 : Liste des images à convertir sur XnConvert"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;2- Cliquer sur l'onglet "Sortie" puis :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Indiquer "{Filename}.png" ou "{Filename}.jpeg" dans le champ "Nom du fichier". Le choix de ce formatage sera important pour la suite 😉&lt;/li&gt;
&lt;li&gt;Choisir "WEBP-Webp" dans le bloc format&lt;/li&gt;
&lt;li&gt;Décocher la case "Préserver les métadonnées" et cocher "Préserver l'extension"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kTY0XFyt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/etqizxiclexbci9ns767.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kTY0XFyt--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/etqizxiclexbci9ns767.png" alt="Etape 2 : choix du format, nom du fichier... sur XnConvert"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;3- Pour finir, cliquer sur "Convertir" en bas à droite de la fenêtre :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sd6GTXUp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dchkm40ngo098wh1gjzm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sd6GTXUp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/dchkm40ngo098wh1gjzm.png" alt="Etape 3 : fenêtre de confirmation des conversions sur XnConvert"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nous obtenons ainsi une image avec le nom : [nom-image].jpeg.webp (ou [nom-image].png.webp). Dans notre exemple, le poids est passé de 181 Ko à 149 Ko.&lt;/p&gt;
&lt;h4&gt;
  
  
  1.4.1 - Cohabitation entre Webp et PNG/JPEG avec &lt;code&gt;&amp;lt;figure&amp;gt;&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Introduite en 2015, la balise &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; permet de spécifier plusieurs règles afin que le navigateur puisse déterminer l'image la plus pertinente à télécharger. Plusieurs conditions sont possibles : densité de pixels, dimensions du navigateur... et le format d'image 🙂.&lt;/p&gt;

&lt;p&gt;Pour utiliser &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt;, il suffit de lui associer plusieurs &lt;code&gt;&amp;lt;source&amp;gt;&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;picture&amp;gt;
  &amp;lt;source srcset="chat-mignon.webp" type="image/webp"&amp;gt;
  &amp;lt;source srcset="chat-mignon.jpeg" type="image/jpeg"&amp;gt;
  &amp;lt;img src="chat-mignon.jpeg" alt="" /&amp;gt;
&amp;lt;/picture&amp;gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Dans cet exemple, le navigateur optera pour le format Webp s'il le gère et JPEG dans le cas contraire. L'usage de &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; est donc d'une grande simplicité 🙂.&lt;/p&gt;

&lt;p&gt;Malheureusement, il nous reste le cas d'une image importée via CSS et malheureusement, il n'existe pas d'équivalents à &lt;code&gt;&amp;lt;picture&amp;gt;&lt;/code&gt; pour le CSS.&lt;/p&gt;

&lt;p&gt;La solution est alors d'utiliser JavaScript pour déterminer ce support et notamment via ce snippet (proposé par &lt;a href="https://twitter.com/jaffathecake/status/893109104205496320"&gt;Jake Archibald&lt;/a&gt;) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function supportsWebp() {
  if (!self.createImageBitmap) return false;

  const webpData = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=';
  const blob = await fetch(webpData).then(r =&amp;gt; r.blob());
  return createImageBitmap(blob).then(() =&amp;gt; true, () =&amp;gt; false);
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;En fonction de la réponse, vous pourrez ajouter une classe &lt;code&gt;webp&lt;/code&gt; ou &lt;code&gt;no-webp&lt;/code&gt; à la balise &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; pour, ensuite, au niveau du CSS, utiliser cette classe pour appeler la bonne image :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.no-webp .cat-background {
  background-image: url("chat-mignon.jpg");
}

.webp .cat-background {
  background-image: url("chat-mignon.webp");
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h4&gt;
  
  
  1.4.2 - Cohabitation entre Webp et PNG/JPEG avec nginx
&lt;/h4&gt;

&lt;p&gt;Si vous ne souhaitez pas convertir l'ensemble de vos balises &lt;code&gt;&amp;lt;img /&amp;gt;&lt;/code&gt; de votre projet ou d'utiliser une classe à la balise &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;, il est possible de délivrer le bon format d'image en fonction du support du navigateur et cela grâce à Nginx.&lt;/p&gt;

&lt;p&gt;Pour obtenir ce comportement, la premier prérequis sera de faire cohabiter l'image JPEG/PNG à côté de son équivalent Webp. Nous obtenons ainsi :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;images/
  - chat.jpeg
  - chat.jpeg.webp
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Dans un second temps, nous utiliserons l'instruction &lt;code&gt;map&lt;/code&gt; de Nginx pour créer une variable &lt;code&gt;$webp_suffix&lt;/code&gt;. Cette dernière contiendra &lt;code&gt;.webp&lt;/code&gt; ou sera vide en fonction du support de Webp par le navigateur. Ce support sera déterminé suivant le contenu du &lt;em&gt;header&lt;/em&gt; HTTP &lt;code&gt;Accept&lt;/code&gt; de la requête :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight nginx"&gt;&lt;code&gt;    &lt;span class="c1"&gt;# Detect if browser accepts .webp images ?&lt;/span&gt;
    &lt;span class="k"&gt;map&lt;/span&gt; &lt;span class="nv"&gt;$http_accept&lt;/span&gt; &lt;span class="nv"&gt;$webp_suffix&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kn"&gt;default""&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;"~*webp"&lt;/span&gt; &lt;span class="s"&gt;".webp"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Pour finir, nous utiliserons la commande &lt;code&gt;try_files&lt;/code&gt; de Nginx, permettant de tester la présence de plusieurs fichiers successivement et renvoyer le premier trouvé.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight nginx"&gt;&lt;code&gt;    &lt;span class="c1"&gt;# Handle .webp&lt;/span&gt;

    &lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;~&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt; &lt;span class="s"&gt;^/assets/.+&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s"&gt;.(jpe?g|png)&lt;/span&gt;$ &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kn"&gt;root&lt;/span&gt; &lt;span class="n"&gt;/home/deploy/build/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="kn"&gt;try_files&lt;/span&gt; &lt;span class="nv"&gt;$uri$webp_suffix&lt;/span&gt; &lt;span class="nv"&gt;$uri&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ainsi, nous testerons dans l'ordre :&lt;br&gt;
1 - l'URL de la ressource concaténée avec la valeur &lt;code&gt;$webp_suffix&lt;/code&gt; pouvant donc être &lt;code&gt;&amp;lt;nom-image&amp;gt;.jpeg.webp&lt;/code&gt; ou &lt;code&gt;&amp;lt;nom-image&amp;gt;.jpeg&lt;/code&gt;&lt;br&gt;
2 - l'URL demandée en solution de secours (en cas d'absence du fichier Webp notamment)&lt;br&gt;
3 - Erreur 404&lt;/p&gt;

&lt;p&gt;Nous obtenons un système faisant cohabiter à la fois les images au format JPEG/PNG et Webp, basée sur la prise en charge du format par le navigateur de l'utilisateur.&lt;/p&gt;

&lt;p&gt;D'ailleurs, on constate sur la console de Google Chrome que le fichier reçu est bien une image Webp :&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--372o7SMk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/oxmf2uwsadogowh7up55.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--372o7SMk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/oxmf2uwsadogowh7up55.png" alt="Console Chrome avec format de retour en Webp"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  1.5 - &lt;em&gt;Lazy-loading&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;Les images les plus légères restent celles qu'on ne télécharge pas. Pour cela, le &lt;em&gt;lazy-loading&lt;/em&gt; est la solution toute nommée et consiste à télécharger une ressource seulement si cela s'avère nécessaire. Dans le cas d'une image, l'élément déclenchant son téléchargement sera ainsi son apparition - ou sa proche apparition - dans la zone d'affichage de la page Web.&lt;/p&gt;
&lt;h4&gt;
  
  
  1.5.1 - Avec &lt;em&gt;Lazysizes&lt;/em&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://github.com/aFarkas/lazysizes"&gt;Lazysizes&lt;/a&gt; est une librairie JavaScript nous facilitant la mise en place du &lt;em&gt;lazy loading&lt;/em&gt; d'images. D'une taille compressée de 3.2Ko, son utilisation se veut assez simple en renommant notre attribut &lt;code&gt;src&lt;/code&gt; en &lt;code&gt;data-src&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;data-src=&lt;/span&gt;&lt;span class="s"&gt;"static/logo.jpg"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lazyload"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;De plus, &lt;em&gt;Lazysize&lt;/em&gt; nous permet aussi d'adapter les images en fonction de l'appareil de l'utilisateur (mobile, tablette ou PC).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;    &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt;
      &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;
      &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"lazyload"&lt;/span&gt;
      &lt;span class="na"&gt;data-srcset=&lt;/span&gt;&lt;span class="s"&gt;"small.jpg 500w, medium.jpg 640w, big.jpg 1024w"&lt;/span&gt;
      &lt;span class="na"&gt;data-src=&lt;/span&gt;&lt;span class="s"&gt;"medium.jpg"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Simple mais terriblement efficace 🙂&lt;/p&gt;

&lt;h4&gt;
  
  
  1.5.2 - Avec l'attribut &lt;code&gt;loading&lt;/code&gt; de &lt;code&gt;img&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;La balise &lt;code&gt;img&lt;/code&gt; accueille depuis peu un nouvel attribut &lt;code&gt;loading&lt;/code&gt; qui, couplé avec la valeur &lt;code&gt;lazy&lt;/code&gt;, permet d'implémenter nativement un système de &lt;em&gt;lazy loading&lt;/em&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"chat-mignon.png"&lt;/span&gt; &lt;span class="na"&gt;loading=&lt;/span&gt;&lt;span class="s"&gt;"lazy"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fmZxL95A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jkhxn3vhaatp88vkfbjh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fmZxL95A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/jkhxn3vhaatp88vkfbjh.png" alt="Un support pas top..."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sa prise en compte est donc loin d'être garantie...  Toutefois, vous ne perdez rien à implémenter ce nouvel attribut qui sera tout simplement ignoré par les navigateurs non compatibles 🙂 (qui téléchargeront donc directement l'image).&lt;/p&gt;




&lt;h2&gt;
  
  
  🚨 2 - Les polices personnalisées
&lt;/h2&gt;

&lt;p&gt;Relativement récent dans l'histoire du Web, l'ajout des polices personnalisées apporte une touche de personnalisation supplémentaire au design de nos pages. Et même s'elles sont minimes, des optimisations restent possibles.&lt;/p&gt;

&lt;h3&gt;
  
  
  2.1 - Fonctionnement des polices personnalisées
&lt;/h3&gt;

&lt;p&gt;Avant de nous attarder sur les améliorations de performances possibles, il est nécessaire de présenter le fonctionnement des polices personnalisées.&lt;/p&gt;

&lt;p&gt;Tout d'abord, l'inclusion d'une police peut se réaliser de plusieurs manières mais les deux méthodes les plus courantes sont :   &lt;/p&gt;

&lt;p&gt;1- L'import direct de la police depuis notre serveur&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Open Sans"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("/fonts/OpenSans-Regular-webfont.woff2")&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;"woff2"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="sx"&gt;url("/fonts/OpenSans-Regular-webfont.woff")&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;"woff"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;2- Via une ressource externe (ici, Google Fonts) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="nt"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"https://fonts.googleapis.com/css2?family=Open+Sans:wght@300"&lt;/span&gt; &lt;span class="nt"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"stylesheet"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Dans tous les cas, l'utilisation dans nos styles CSS est identique :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Open Sans"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Bien entendu, la police n'est pas directement présente sur le navigateur et doit donc être téléchargée. Mais que se passe-t-il pendant ce temps au niveau de l'affichage ? Figurez-vous que le navigateur n'affiche tout simplement...rien ! Plus exactement, il compense l'absence de la police en la remplaçant par une police invisible dont la taille se veut proche du résultat final (via &lt;em&gt;font-size&lt;/em&gt; et &lt;em&gt;font-weight&lt;/em&gt; principalement).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--D9FpbMww--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pf7791f86mz914nbh4wc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--D9FpbMww--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pf7791f86mz914nbh4wc.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Par défaut, cette situation va durer 3 secondes (sur Firefox et Chrome tout du moins) avant que le navigateur bascule sur la seconde police (&lt;em&gt;serif&lt;/em&gt; dans notre exemple). Trois secondes pendant lesquelles l'utilisateur ne pourra lire le contenu de votre site...&lt;/p&gt;

&lt;p&gt;Bien sûr, dès que la police sera téléchargée, l'affichage de votre page sera mis à jour (que ce soit avant ou après les 3 secondes de délai).&lt;/p&gt;

&lt;h3&gt;
  
  
  2.2 - Utilisation de &lt;em&gt;display: swap&lt;/em&gt;
&lt;/h3&gt;

&lt;p&gt;La règle CSS &lt;code&gt;font-display&lt;/code&gt; permet de modifier la politique d'import des polices personnalisées, sa valeur par défaut est &lt;code&gt;auto&lt;/code&gt; et correspond au scénario décrit précédemment.&lt;/p&gt;

&lt;p&gt;En remplaçant &lt;code&gt;auto&lt;/code&gt; par &lt;code&gt;swap&lt;/code&gt;, le délai de basculement vers la police de remplacement passe alors de 3 secondes à 100 ms ! Un gain de temps appréciable et réduisant ainsi le &lt;em&gt;Time to first meaningful paint&lt;/em&gt;, métrique définie par Google correspondant au temps d'apparition du premier contenu pertinent.&lt;/p&gt;

&lt;p&gt;Son utilisation est extrêmement simple.&lt;/p&gt;

&lt;p&gt;Dans notre premier cas d'import, nous ajoutons simplement &lt;code&gt;font-display: swap;&lt;/code&gt; dans le bloc &lt;code&gt;@font-face&lt;/code&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;  &lt;span class="k"&gt;@font-face&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;"Open Sans"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="nl"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("/fonts/OpenSans-Regular-webfont.woff2")&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;"woff2"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="sx"&gt;url("/fonts/OpenSans-Regular-webfont.woff")&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;"woff"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="py"&gt;font-display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;swap&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Pour Google Fonts, depuis mai 2019, il suffit d'ajouter '&amp;amp;display=swap' dans l'URL d'import pour obtenir, par exemple :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;link&lt;/span&gt; &lt;span class="nt"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"https://fonts.googleapis.com/css2?family=Open+Sans:wght@300&amp;amp;display=swap"&lt;/span&gt; &lt;span class="nt"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"stylesheet"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Si vous souhaitez gérer plus finement votre police d'import de polices, &lt;code&gt;font-display&lt;/code&gt; autorise d'autres valeurs :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;font-display: block&lt;/strong&gt;&lt;br&gt;
Cette option est semblable au fonctionnement par défaut : 3 secondes de police invisible avant de basculer sur la police de remplacement. Son seul intérêt est de s'assurer que nous ayons bien ces 3 secondes de délai, étant donné que tous les navigateurs peuvent avoir une politique &lt;code&gt;auto&lt;/code&gt; différente comparée à celle de Firefox et Chrome.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;font-display: fallback&lt;/strong&gt;&lt;br&gt;
L'option &lt;code&gt;fallback&lt;/code&gt; réduit aussi le délai de basculement vers la police de remplacement à 100 ms mais limite aussi la durée de récupération de la police personnalisée à 3 secondes (là où &lt;code&gt;swap&lt;/code&gt; n'impose pas de durée de temps). Elle permet donc d'indiquer au navigateur d'arrêter la récupération de la police si son téléchargement s'avère trop long.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;font-display: optional&lt;/strong&gt;&lt;br&gt;
Plus radicale que &lt;code&gt;fallback&lt;/code&gt;, elle réduit le délai de basculement à 100 ms mais arrête la récupération de la police par la même occasion. Traduction pour le navigateur: "Si tu as la police sous la main (donc en cache) tu l'utilises, sinon inutile de la récupérer".&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Fin de la première partie
&lt;/h2&gt;

&lt;p&gt;Nous voici arrivés à la fin de cette première partie consacrée aux images et aux polices personnalisées.&lt;br&gt;
Dans le prochain épisode, nous aborderons les optimisations liées JavaScript et le pré-chargement de ressources.&lt;/p&gt;

&lt;p&gt;Restez connecté 🙂.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔰 Aller plus loin
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Vidéos et articles
&lt;/h3&gt;

&lt;p&gt;Voici un ensemble de ressources pour explorer l'univers de l'optimisation des chargements :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Fast load times : &lt;a href="https://web.dev/fast/"&gt;https://web.dev/fast/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Lazy-loading d'images et &lt;em&gt;iframe&lt;/em&gt; : &lt;a href="https://web.dev/native-lazy-loading/"&gt;https://web.dev/native-lazy-loading/&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Web performance made easy (Google I/O '18) :&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=Mv-l3-tJgGk"&gt;https://www.youtube.com/watch?v=Mv-l3-tJgGk&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Speed at Scale: Web Performance Tips and Tricks from the Trenches (Google I/O ’19) : &lt;a href="https://www.youtube.com/watch?v=YJGCZCaIZkQ"&gt;https://www.youtube.com/watch?v=YJGCZCaIZkQ&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Polices personnalisées : &lt;br&gt;
&lt;a href="https://developers.google.com/web/updates/2016/02/font-display"&gt;https://developers.google.com/web/updates/2016/02/font-display&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Sur Twitter
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Houssein Djirdeh : &lt;a href="https://twitter.com/hdjirdeh"&gt;https://twitter.com/hdjirdeh&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Addy Osmani : &lt;a href="https://twitter.com/addyosmani"&gt;https://twitter.com/addyosmani&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Katie Hempenius : &lt;a href="https://twitter.com/katiehempenius"&gt;https://twitter.com/katiehempenius&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Paul Irish : &lt;a href="https://twitter.com/paul_irish"&gt;https://twitter.com/paul_irish&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Lighthouse : &lt;a href="https://twitter.com/____lighthouse"&gt;https://twitter.com/____lighthouse&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>frontend</category>
      <category>optimization</category>
      <category>coding</category>
    </item>
    <item>
      <title>Parcours d'arbre : Depth First &amp; Breadth First 🌳</title>
      <dc:creator>Alexandre CANTIN</dc:creator>
      <pubDate>Thu, 30 Apr 2020 11:11:25 +0000</pubDate>
      <link>https://dev.to/alexandrecantin/parcours-d-arbre-depth-first-breadth-first-1g3l</link>
      <guid>https://dev.to/alexandrecantin/parcours-d-arbre-depth-first-breadth-first-1g3l</guid>
      <description>&lt;p&gt;Le parcours d'arbre est un grand classique lors d'entretiens algorithmiques et bien que cet exercice peut sembler intimidant aux premiers abords, nous allons découvrir qu'il y a, en réalité, plus de peur que de mal !&lt;/p&gt;

&lt;p&gt;Ainsi, dans cet article, nous nous intéressons aux deux méthodes les plus courantes afin de parcourir un arbre que sont :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;le parcours en profondeur (&lt;em&gt;Depth First&lt;/em&gt;), où le choix du prochain noeud se porte sur le premier enfant du noeud courant :&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JS5gCduV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vdurkljcqrtfpce2k25r.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JS5gCduV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vdurkljcqrtfpce2k25r.gif" alt="Parcours en profondeur"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;le parcours en largeur (&lt;em&gt;Breadth First&lt;/em&gt;), où le prochain noeud est celui adjacent au noeud courant, c'est-à-dire celui situé au même niveau de profondeur :&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--x277Ynzn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5ygukkr8rbkl6z60pz07.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--x277Ynzn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5ygukkr8rbkl6z60pz07.gif" alt="Parcours en largeur"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Parcours en profondeur - &lt;em&gt;Depth first&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Au niveau algorithmique, l'utilisation de la récursivité reste le choix le plus pertinent. Ainsi, nous utiliserons la récursivité pour notre parcours mais nous y ajouterons toutefois une étape supplémentaire, pour les besoins de l'article, en stockant les noeuds dans une structure de données.&lt;br&gt;
En effet, dès l'entrée dans un noeud et après le traitement de sa valeur, nous stockerons ses enfants dans une structure de données (contenant une méthode &lt;em&gt;saveNode&lt;/em&gt;), récupérerons ensuite le prochain noeud (méthode &lt;em&gt;getNextNode&lt;/em&gt;) et passerons à la prochaine étape de notre parcours. En pseudo-code, nous obtenons ainsi :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Structure de données encore inconnue
class Structure {
  saveNode(node) {}
  getNextNode() {}
}

// Fonction de traitement d'un noeud
var nodeStructure = new Structure()
function computeNode(node) {
  if(!node) return;

  computeNodeValue(node.value)

  if(node.left) nodeStructure.saveNode(node.left)
  if(node.right) nodeStructure.saveNode(node.right)

  computeNode(nodeStructure.getNextNode())
}

// Création de l'arbre et début du parcours
var tree = new Tree(...)
computeNode(tree.root)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Maintenant, si nous déroulons un exemple plus concret avec l'arbre présenté en début d'article et le pseudo-code ci-dessus :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1ère étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 1, nous sommes à la racine&lt;/li&gt;
&lt;li&gt;Nous ajoutons ses deux enfants dans la structure de données : &lt;code&gt;[2,5]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[2,5]&lt;/code&gt; =&amp;gt; 2, &lt;code&gt;[5]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 2&lt;/li&gt;
&lt;li&gt;Nous ajoutons ses deux enfants dans la structure de données : &lt;code&gt;[3, 4, 5]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[3, 4, 5]&lt;/code&gt; =&amp;gt; 3, &lt;code&gt;[4,5]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 3&lt;/li&gt;
&lt;li&gt;Pas d'enfant, la structure est inchangée : &lt;code&gt;[4, 5]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[4, 5]&lt;/code&gt; =&amp;gt; 4, &lt;code&gt;[5]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 4&lt;/li&gt;
&lt;li&gt;Pas d'enfant, la structure est inchangée : &lt;code&gt;[5]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[5]&lt;/code&gt; =&amp;gt; 5, &lt;code&gt;[]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 5&lt;/li&gt;
&lt;li&gt;Nous ajoutons ses deux enfants dans la structure de données : &lt;code&gt;[6,7]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[6,7]&lt;/code&gt; =&amp;gt; 6, &lt;code&gt;[7]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 6&lt;/li&gt;
&lt;li&gt;Pas d'enfant, la structure est inchangée : &lt;code&gt;[7]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[7]&lt;/code&gt; =&amp;gt; 7, &lt;code&gt;[]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 7&lt;/li&gt;
&lt;li&gt;Pas d'enfant, la structure est inchangée : &lt;code&gt;[]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[]&lt;/code&gt; =&amp;gt; x, &lt;code&gt;[]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;8ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pas de valeur et la structure est vide : le parcours est terminée&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Après ce long déroulé, nous constatons que l'ordre d'ajout dans la liste à son importance : nous ne prenons que le noeud inséré le plus récemment. En algorithmique, ce concept est appelé &lt;em&gt;Last In First Out&lt;/em&gt;, abrégé &lt;em&gt;LIFO&lt;/em&gt;, et notre structure de données se doit de respecter cette règle pour devenir compatible avec le parcours en profondeur; comme par exemple une &lt;em&gt;stack&lt;/em&gt;.&lt;br&gt;
Un ajustement est juste nécessaire vis-à-vis du pseudo algorithme précédent : le noeud gauche y est inséré avant le noeud de droite. On parcourt ainsi l'arbre par la droite (1 - 5 - 7 - 6 - 2 - 4 - 3) et on doit ainsi permuter les deux lignes &lt;em&gt;saveNode&lt;/em&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Noeud droite avant le gauche
if(node.right) nodeStructure.saveNode(node.right)
if(node.left) nodeStructure.saveNode(node.left)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Nous en avons ainsi fini avec le parcours en profondeur. L'ajout d'une structure de donnée peut sembler au premier abord superflu mais, rassurez-vous, ce socle nous sera très utile pour le parcours en largeur.&lt;/p&gt;

&lt;h2&gt;
  
  
  Parcours en largeur - &lt;em&gt;Breadth First&lt;/em&gt;
&lt;/h2&gt;

&lt;p&gt;Pour le parcours en largeur, nous utiliserons une base identique à celle du parcours en profondeur, à savoir le pseudo-algorithme associé à une structure de données (encore inconnue à ce stade).&lt;br&gt;
Si, comme précédent, nous effectuons un déroulé des différentes étapes du parcours, nous obtenons :&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1ère étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 1, nous sommes à la racine&lt;/li&gt;
&lt;li&gt;Nous ajoutons ses deux enfants dans la structure de données : &lt;code&gt;[2,5]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[2,5]&lt;/code&gt; =&amp;gt; 2, &lt;code&gt;[5]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 2&lt;/li&gt;
&lt;li&gt;Nous ajoutons ses deux enfants dans la structure de données : &lt;code&gt;[3, 4, 5]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[3, 4, 5]&lt;/code&gt; =&amp;gt; 5, &lt;code&gt;[3, 4]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 5&lt;/li&gt;
&lt;li&gt;Nous ajoutons ses deux enfants dans la structure de données : &lt;code&gt;[3, 4, 6, 7]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément :  &lt;code&gt;[3, 4, 6, 7]&lt;/code&gt; =&amp;gt; 3, &lt;code&gt;[4, 6, 7]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 3&lt;/li&gt;
&lt;li&gt;Pas d'enfant, la structure est inchangée : &lt;code&gt;[4, 6, 7]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[4, 6, 7]&lt;/code&gt; =&amp;gt; 4, &lt;code&gt;[6, 7]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 4&lt;/li&gt;
&lt;li&gt;Pas d'enfant, la structure est inchangée : &lt;code&gt;[6,7]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[6,7]&lt;/code&gt; =&amp;gt; 6, &lt;code&gt;[7]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;6ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 6&lt;/li&gt;
&lt;li&gt;Pas d'enfant, la structure est inchangée : &lt;code&gt;[7]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément :  &lt;code&gt;[7]&lt;/code&gt; =&amp;gt; 7, &lt;code&gt;[]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;7ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nous traitons la valeur du noeud : 7&lt;/li&gt;
&lt;li&gt;Pas d'enfant, la structure est inchangée : &lt;code&gt;[]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Nous récupérons le prochain élément : &lt;code&gt;[]&lt;/code&gt; =&amp;gt; x, &lt;code&gt;[]&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;8ème étape :&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pas de valeur et la structure est vide : le parcours est terminée&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ici, et contrairement au parcours en profondeur, ce n'est pas le dernier noeud inséré qui nous intéresse mais le plus ancien. Ce concept algorithmique, appelé &lt;em&gt;First In First Out&lt;/em&gt; et abrégé &lt;em&gt;FIFO&lt;/em&gt;,  se doit donc d'être respecté par notre structure de données et comme exemple, nous pouvons citer une queue, cette dernière étant similaire à une file d'attente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Plusieurs domaines de l’informatique exploitent la puissance des arbres : index de base de données, arbres de décision, analyse de code source (&lt;em&gt;Abstract syntax tree&lt;/em&gt;)... et cela explique ainsi leur présence en entretien technique. &lt;/p&gt;

&lt;p&gt;Toutefois comme nous venons de le voir, le parcours d'arbre en profondeur ou en largeur n’est pas si complexe et peut se résumer à une simple association avec une structure de données :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;une structure respectant la logique &lt;em&gt;LIFO&lt;/em&gt; pour un parcours en profondeur&lt;/li&gt;
&lt;li&gt;et une structure respectant la logique &lt;em&gt;FIFO&lt;/em&gt; pour un parcours en largeur&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deux concepts finalement faciles et rapides à retenir et qui, j'espère, ne vous fera plus craindre de futurs entretiens avec des parcours d'arbres !&lt;/p&gt;

</description>
      <category>algorithme</category>
    </item>
  </channel>
</rss>
