Accélérer vos pages Web : quelques réflexes ⚡️ (partie 2)
Tags: optimization, coding, frontend
Published: 2020-07-22 06:42:56


Cet article sera le deuxième d'une série de trois :

  1. Images et polices personnalisées ([lire l'article](https://dev.to/alexandrecantin/optimiser-vos-pages-web-quelques-reflexes-indispensables-g79))
  2. JavaScript et pré-chargement de ressources (cet article)
  3. Contenus animés et LightHouse (à venir)

---

## 🃏 3 - JavaScript
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 _Single Page Application_ : Angular, Vue et React.

### 3.1 - Code splitting
Le terme SPA (_Single Page Application_) résume à lui tout seul son principe sous-jacent : l'ensemble de notre application est contenu dans une unique page - et par conséquent **dans un seul fichier JavaScript**, dont la taille est forcément corrélée au nombre de fonctionnalités proposées par votre site.

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 _router_. Dans l'univers de React, le _router_ le plus utilisé et connu est le [https://github.com/ReactTraining/react-router/](react-router-dom) :

```javascript
import React from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';

import Home from './routes/Home';
import About from './routes/About';

const App = () => (
  <Router>
    <Switch>
      <Route exact path="/" component={Home}/>
      <Route path="/a-propos" component={About}/>
    </Switch>
  </Router>
);
```

Note : des équivalents existent pour Svelte / Vue / Angular mais nous nous focaliserons sur React ici 🙂

Dans cet exemple, notre _bundle_ 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 _router_, pouvant se compter par dizaines.
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 !

Pour pallier ce problème, le code _splitting_ a vu le jour. Ce dernier permet de subdiviser notre _bundle_ en différentes sous-divisions pour n'envoyer aux utilisateurs que le strict nécessaire pour la page courante.

Dans l'écosystème React, cette possibilité nous est offerte par la combinaison de deux fonctions internes :
-  _lazy_ : prenant en paramètre un import dynamique, qui sera ici notre composant : `const About = lazy(() => import('./routes/About'));`

- _Suspense_ : composant React dont l'objectif est d'attendre la résolution d'une promesse, l'import dynamique de _lazy_ dans notre cas. _Suspense_ accepte aussi un paramètre `fallback` pour indiquer un texte ou un composant à exécuter en attendant la résolution de notre promesse :
```html
<Suspense fallback={<div>Chargement...</div>}>
  ...
</Suspense>
```

Au final, afin d'obtenir un code-splitting fonctionnel, nous devons adapter l'exemple précédent comme suit :

```javascript
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));

const App = () => (
  <Router>
    <Suspense fallback={<div>Chargement...</div>}>
      <Switch>
        <Route exact path="/" component={Home}/>
        <Route path="/a-propos" component={About}/>
      </Switch>
    </Suspense>
  </Router>
);
```

Bien sûr, cela peut sembler inutile de réaliser du _code-splitting_ sur un site contenant deux pages 🙂 mais rassurez-vous, l'effet est démultiplié au fur à mesure que le nombre de pages augmente. 

### 3.2 - Poids des librairies externes

#### 3.2.1 - _Bundle Phobia_
NPM a permis l'introduction de bon nombre de librairies que nous pouvons inclure et utiliser dans notre JavaScript côté _front_. 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.

Pour cela, je ne peux que vous conseiller le site [BundlePhobia](https://bundlephobia.com/) dont le nom est éloquent : le site est à destination des développeurs craignant d'alourdir le poids de leur _bundle_.

![Page d'accueil de BundlePhobia](https://dev-to-uploads.s3.amazonaws.com/i/5vj5sizhr8c0ti9dmz46.png)

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 :

![Statistiques de bundlePhobia](https://dev-to-uploads.s3.amazonaws.com/i/2edv1gawvlrse9twlnno.png)

N'oubliez pas de vérifier si la librairie en question est aussi _tree-shakable_. Une librairie _tree-shakable_ est une librairie dont les parties non utilisées seront retirées du _bundle_ final, occasionnant donc une réduction du poids de nos fichiers JavaScript. Par chance, BundlePhobia nous l'indique 🥳

Pour finir, nous avons aussi une liste de librairies alternatives offrant des fonctionnalités similaires :

![Autres librairies possibles](https://dev-to-uploads.s3.amazonaws.com/i/qqflia70wbn3cjan6uqw.png)


#### 3.2.2 - _Import Cost_
Afin de surveiller le poids de votre librairie d'une manière plus régulière, une extension _Visual Studio Code_ nommée _Import Cost_ vous indique le poids des librairies que vous incorporez dans votre code :

![Exemple dans Visual Studio Code](https://dev-to-uploads.s3.amazonaws.com/i/c7azw7hdg5ynr97bajth.gif)

Vous trouverez cette extension facilement dans le _MarketPlace_ de _Visual Studio Code_ :

![Import cost](https://dev-to-uploads.s3.amazonaws.com/i/6qg70uhash6yun8fqnei.png)

## 💠  4 - Préchargement de ressources secondaires avec `<link rel="preload" />`

### 4.1 - Fonctionnement de `preload`
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/_frameworks_ comme React, Vue, Angular ou encore Svelte.

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.

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 `<link rel="preload" />` :

```html
<link rel="preload" href="/fonts/custom-font.woff2" as="font">
```

Dans cet exemple, nous indiquons à notre navigateur de pré-télécharger le fichier `/fonts/custom-font.woff2` car nous en aurons besoin *pour le premier affichage de notre page*. 
J'insiste lourdement sur le terme *pour le premier affichage de notre page*. 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 🙂.

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 _onload_) :

![Message d'avertissement de Chrome](https://dev-to-uploads.s3.amazonaws.com/i/x1hc9j9c7i6tznwwb7pr.png)

### 4.2 - Spécifications

Au niveau de sa composition, cette balise autorise les attributs :
-  `rel` : contenant "preload" pour indiquer qu'on souhaite un pré-chargement d'une ressource (d'autres valeurs sont possibles mais on reviendra dessus plus tard)
- `href` : indiquant le lien de la ressource à récupérer, qui peut être un lien interne ou externe (Google Font par exemple).
- `as` : pour indiquer le type de ressource : "font", "script", "style", "video", "image"... (liste complète disponible [à cette adresse](https://fetch.spec.whatwg.org/#concept-request-destination)). L'ajout et la complétion de cet attribut a une très grande importance : elle permet au navigateur d'appliquer la bonne _Content Security Policy_, d'utiliser les bons _header_ HTTP mais aussi de mieux identifier la ressource pour éviter sa double récupération.
- `crossorigin` (optionnel) : permettant de gérer les _Cross-origin resource sharing_ (CORS) et devant contenir obligatoirement "origin" pour les polices afin d'éviter un double téléchargement (même depuis [une origine équivalente](https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/)). 
- `type` (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: `.webp` et Safari)
- `media` : pour ne déclencher le téléchargement que sous certaines conditions :
```html
<link rel="preload" as="image" href="small-background.jpg" media="(max-width: 800px)">
<link rel="preload" as="image" href="large-background.jpg" media="(min-width: 801px)">  
```

### 4.3 - Support navigateur
Au niveau de son support, et aussi bizarre que cela puisse paraître, seul Firefox ne supporte pas nativement l'attribut `rel="preload"` 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 🙂.

![rel="preload" sur Firefox n'est qu'une fonctionnalité expérimental](https://dev-to-uploads.s3.amazonaws.com/i/gu6irx7sqi42hthcsoeh.png)


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

Restez connecté 🙂.

---

## 🔰 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/

- Preload, Prefetch And Priorities in Chrome : https://medium.com/reloading/preload-prefetch-and-priorities-in-chrome-776165961bbf

- Preload: What Is It Good For? : https://www.smashingmagazine.com/2016/02/preload-what-is-it-good-for/

- 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