Webpack, un bundler pour les gouverner tous (1/3)

Eric Burel
PointJS
Published in
7 min readNov 14, 2019

--

Les outils — Épisode 2

<script>, votre nouveau pire ennemi

Le protocole HTTP est un protocole simple et fiable. Certes. Mais c’est aussi un protocole lourd, car les connexions ne sont pas persistantes. Chaque requête nécessite donc d’établir une connexion avec le serveur, qui sera détruite à la réception de la réponse.

Une application frontend de taille moyenne va utiliser entre plusieurs dizaines et plusieurs centaines de librairies externes en production, qui vont chacune déclencher au moins une requête HTTP pour être chargée.

C’est un peu comme si votre chanteur préféré commençait son concert à l’Olympia en serrant la main de chaque personne présente. Ce serait sympa, mais il resterait peu de temps pour la chanson.

Un concert du groupe NetworkTab, avec son tube “Pourquoi ça charge pas”

L’idée est alors de créer un bundle, un fichier unique qui contient la totalité de votre code et qui sera utilisé en production. Vous supprimez alors tout ces bonjours/au revoir inutile et votre page se charge bien plus rapidement.

Une application, c’est du travail

Outre le grand volume de librairies utilisées en production, la mise en place d’un environnement de développement nécessite l’utilisation d’outil multiples : transpiler son code JavaScript ou Sass avec Babel, compresser les feuilles de styles avec Uglify, faire tourner le linter Eslint…

On se rend en fait rapidement compte que l’étape de création d’un bundle est le bon moment pour réaliser toutes ces actions, aussi bien avant une mise en production que lorsque l’on souhaite tester l’application.

Par chance, Webpack va nous permettre de gérer tout ça très facilement. Enfin, moyennant quand même un peu de configuration…

Webpack, votre homme à tout faire

Entrée, sortie, rien de plus simple

Webpack est par définition un static module bundler, ni plus ni moins. Son rôle est d’analyser tous les imports dans votre code (import ES6, require, voire même les imports réalisés dans les feuilles de styles) et de tous les réunir pour construire votre bundle.

Le pouvoir des loaders

Webpack va donc lire un par un tous les fichiers utiles à votre code. L’intelligence de Webpack réside dans la manière dont le fichier sera lu. Son fonctionnement est configurable et extensible, via les loaders.

On peut par exemple écrire un loader qui, quand il rencontre un impressionnant fichier .scss, le transforme au passage en un inoffensif fichier .css compressé et lisible sur le navigateur. On peut aussi créer un loader qui transforme l'ES6 en ES5 via Babel, ou qui traduit le TypeScript en JavaScript, un loader qui créer une police d’écriture à partir de fichiers .svg... les possibilités sont en fait illimitées !

Par chance, il existe un nombre incalculable de loaders de très bonne qualité disponibles en open source. Écrire un nouveau loader est aussi tout à fait possible.

🔨 Mon premier bundle

Ma micro-application

Créons, pour l’exemple, trois fichiers, foo.js, bar.js, app.js.

// foo.js
const foo = 42;
export default foo;
// bar.js
const bar = 3.14;
export default bar;
// app.js
import foo from './foo.js';
import bar from './bar.js';
console.log("foobar:", foo, bar);

Le fichier app.js est le cœur de notre application. On l’appelle parfois aussi main ou index.

Ma config

Et voici la configuration Webpack la plus petite du monde. Créez un fichier webpack.config.js et remplissez le comme suit:

module.exports = {
entry: "./app.js",
mode: "development",
output: {
filename: "bundle.js"
}
}

Tout est prêt, on peut désormais installer Webpack de manière globale, puis l’exécuter.

npm i -g webpack@4.41.2 webpack-cliwebpack

Note : on installe ici la version 4 de Webpack. Les loaders ne sont pas nécessairement compatibles d’une version à l’autre et la syntaxe de la configuration peut changer.

Webpack va chercher le fichier webpack.config.js, et créer un bundle en conséquence. A ce stade, vous allez cependant avoir une erreur, car vous n’avez pas configuré de loader.

Brancher Babel

Si vous ne connaissez pas Babel, retrouvez notre point sur ce sujet.

Un loader pour transpiler

Ajoutons de nouvelles fonctionnalités à notre configuration, sous la forme de loaders, par exemple la transpilation du code ES6 en ES5 universel avec Babel.

Par convention, les loaders Webpack s’appellent systématiquement machin-loader. Le loader utilisant Babel est donc... babel-loader, et vous pouvez trouver sa documentation sur GitHub.

Installation de babel-loader

Installons donc ce loader, ainsi que Babel (si ce n’est pas déjà fait) et @babel/preset-env, qui nous permet de configurer facilement le niveau de transpilation (en fonction de si vous souhaitez gérer certains anciens navigateurs type Internet Explorer).

npm install ---save-dev babel-loader @babel/core @babel/preset-env

Ajout de la “règle” de transpilation pour les fichiers .js

Ajoutons le champ suivant à notre webpack.config.js:

module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}

En détails, voici ce que nous venons de faire :

  • le champ module correspond à la configuration des imports de modules, c'est ici que l'on va configurer tout ce qui se passe lorsque Webpack rencontre un import
  • les règles (rules) nous permettent de charger des loaders lorsqu'une condition est remplie
  • le test, c'est une condition sur le nom du fichier importé, sous la forme d'une expression régulière. Ici, on applique la règle à tous les fichiers .js.
  • on exclut certains fichiers, notamment les modules node_modules. En effet, ces modules sont normalement fournis sous la forme d'un bundle transpilé comme il se doit.
  • et enfin, on choisit et on configure notre loader. Ici, les presets permettent de choisir quels navigateurs seront supportés (on a gardé la valeur par défaut dans cet exemple), mais bien entendu ces options changent en fonction du loader.

Résultat: un bundle unique

Si vous relancez Webpack, vous allez voir apparaître un fichier ./dist/bundle.js. Vous pouvez ouvrir ce fichier et constater... qu'il est relativement moche. Mais en creusant vous pouvez voir que vos const sont transformés en var. Babel est en place !

C’est normal, Webpack s’est occupé d’importer le contenu de chacun de vos fichiers, et a au passage créé des commentaires qui sont utiles aux outils d’analyse du bundle, très pratiques pour les grandes applications. On pourra supprimer les commentaires automatiquement en production.

Vous pouvez désormais charger ce fichier bundle.js dans une page HTML index.html, en une seule requête :

<html>
<head>
</head>
<body>
<script src="dist/bundle.js"></script>
</body>
</html>

Loader des images et du style

Mise en place du style

Pour conclure sur les loaders, voici un exemple un peu plus complexe, pour charger des feuilles de styles écrites en SASS.

Installons SASS et le loader correspondant:

npm install sass-loader node-sass style-loader css-loader --save-dev

et créons un fichier style.scss:

$blue: #0000ff;
html{
background-color: $blue;
}

et ajoutons enfin la règle suivante à notre webpack.config.js:

{
test: /\.scss$/,
use: [{
loader: "style-loader" // creates style nodes from JS strings
}, {
loader: "css-loader" // translates CSS into CommonJS
}, {
loader: "sass-loader" // compiles SASS to CSS
}]
}

Vous remarquerez la présence de plusieurs loaders. Cela permet d’en combiner plusieurs pour une seule règle. Ici, il faut compiler le SASS en CSS, puis permettre son chargement dans notre fichier .js avec style-loader.

Un import scss… dans mon .js !

Oui, vous m’avez bien lu : on peut importer du .scss dans un fichier .js grâce à Webpack, et même des images ou du texte brut.

Ajoutez la ligne suivante au fichier app.js:

import './style.scss';

Et recréez le bundle. Ouvrez la page index.html : et voilà, le fond est bleu, sans même que nous ayons à manipuler le moindre fichier .css.

Nous sommes encore loin d’en avoir fini avec Webpack ! Dans les prochains points JS, nous allons créer une configuration adaptée à la mise en production, et installer un serveur de développement pour lancer facilement notre application.

Autres outils et concepts

  • Gulp et Grunt, sont des task runners, pour gérer automatiquement des tâches complexes (compiler les sources, copier des fichiers etc.). Webpack est un outil plus général, mais aussi plus complexe, parfois Grunt ou Gulp peuvent suffire.
  • Dans la lignée des task runners, Brunch est le petit nouveau. Pour les petits projets, il peut suffire et constituer une bonne alternative à Webpack. Il est à la fois simple mais plus puissant que Gulp et Grunt.
  • Rollup est une alternative récente à Webpack, mais moins riche en fonctionnalité et plus complexe à maîtriser. On utilisera plutôt Rollup pour le bundle de librairies ou frameworks.
  • Les imports dynamiques sont une proposition pour faciliter l’import de librairies à chaud (par exemple seulement si l’utilisateur réalise certaines actions). Ils sont déjà utilisables avec Webpack, et peuvent permettre d’alléger la taille de certains bundles.

Sources

--

--

Eric Burel
PointJS

Next.js teacher and course writer. Co-maintainer of the State of JavaScript survey. Follow me to learn new Next.js tricks ✨ - https://nextjspatterns.com/