DEV Community

Cover image for Accélérer vos pages Web : quelques réflexes ⚡️ (partie 2)
Alexandre CANTIN
Alexandre CANTIN

Posted on

Accélérer vos pages Web : quelques réflexes ⚡️ (partie 2)

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

  1. Images et polices personnalisées (lire l'article)
  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/ :

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 :

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

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 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

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

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

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

Vous trouverez cette extension facilement dans le MarketPlace de Visual Studio Code :

Import cost

💠 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" /> :

<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

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). 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).
  • 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 :
<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

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 :

Top comments (0)