Appearance
Webpack & bundlers JS
Présentation
L’arrivée de Node.js a permis d’avoir un outil à la fois capable d’interpréter du code Javascript et de facilement manipuler des fichiers sur une machine. La conjugaison de ces deux fonctionnalités nous a permis d’imaginer le développement de projets avec des syntaxes plus flexibles que l’on pourrait ensuite confier à un librairie pour quelle les traduise dans une syntaxe plus standard et performante.
Ces librairies capables de transformer un ensemble de fichiers en un ou plusieurs fichiers écrits différemment sont nommées des bundlers. En Javascript, le bundler open-source Webpack fait aujourd’hui office de leader incontestable. Mais il existe également d’autres bundlers Javascript qui présentent des qualités différentes, comme Browserify ou Parcel.
Dans ce chapitre, nous allons explorer les fondamentaux de l'utilisation d’un bundler Javascript au travers du bunbler Webpack.
Le concept de bundler
Le rôle d’un bundler est d’identifier un regroupement de fichiers fonctionnant ensemble pour effectuer un certain nombre d’opérations menant à obtenir un autre ensemble de fichiers contenant les mêmes logiques de code. Les fichiers peuvent alors changer de langage de programmation ou rester dans le même langage.
Bundler, compilateur et interpréteur
Dans ce sens, un bundler ressemble beaucoup à un compilateur, utilisé en C++ ou Java par exemple. L’exécution d’un bundler est d’ailleurs généralement appelée une compilation, et se fait en amont de l’utilisation du projet, durant la phase de construction (build) du projet.
Toutefois, il est capitale de distinguer les bundlers et les compilateurs par une différence fondamentale : un compilateur va analyser du code dans un langage de programmation compilé, comme le C++, pour construire un exécutable contenant du code binaire. Un bundler peut traduire un fichier d’un langage script vers un autre, mais le code obtenu restera du code en langage script.
Ce code devra ensuite être confié à une interpréteur, c’est-à-dire un logiciel, généralement codé en langage compilé, qui est capable de traduire en temps réel un certain langage de programmation en commandes qu’il a implémenté dans son langage à lui, c’est-à-dire un langage compilé.
Node.js est par exemple un logiciel codé en C++, contenant des commandes capables de parser du code Javascript pour en déduire un série d’exécutions à réaliser en C++. Il s’agit du coup d’un interpréteur Javascript.
L’action d’un bundler
L’opération de compilation d’un bundler se déroule en plusieurs étapes.
Rassemblement des fichiers concernés
Il va d’abord identifier tous les fichiers à traiter, généralement tous ceux faisant partie d’un projet particulier. Pour cela, il doit généralement connaître le fichier faisant office de point d’entrée dans l’exécution du projet (l’équivalent du main des langages compilés). Il va ensuite identifier récursivement tous les fichiers dont va dépendre ce point d’entrée. Il peut aussi récupérer l’ensemble des fichiers présents dans un ou plusieurs dossiers spécifiques, selon sa configuration.
Les fichiers ainsi récupérés peuvent être de langages différents. Dans un projet Node.js par exemple, le bundler va récupérer des fichiers .js, mais aussi des fichiers .css et .html, des médias (.png, .gif, etc.), ou même parfois des fichiers propres à un framework (.vue).
Traitement unitaire
Le bundler va ensuite parcourir un à un les fichiers récupérés pour y effectuer des opérations définies par sa configuration. Il peut effectuer une série d’opérations différentes en fonction de l’extension des fichiers, ce qui permet de cibler spécifiquement les images, ou les fichiers d’un certain langage.
En Node.js, ce traitement peut nous permettre notamment :
- De traduire la syntaxe des fichiers Javascript pour quelle soit compatible avec d’anciens navigateurs grâce à Babel.js.
- De traduire des fichiers en langage Sass ou Less en fichiers CSS.
- De traduire des fichiers du framework Vue (
.vue) en fichiers Javascript. - De compresser des images pour réduire leur taille.
INFO
👉 Ces opérations ne sont généralement pas réalisées directement par le bundler mais par des librairies tierces auxquelles le bundler fait appel : Babel.js, l’interpréteur Sass, Less ou Vue.js, etc.
Fusion de fichiers
Un bundler va ensuite pouvoir fusionner les fichiers étant dans un même langage.
En Node.js par exemple, sur un projet de Single Page Application, on va souvent faire le choix de regrouper l’ensemble du code Javascript dans un fichier unique. De même pour le code CSS.
WARNING
Attention toutefois, si cette fusion offre des possibilités d’optimisations supplémentaires, elles induit également que l’ensemble du code soit toujours transféré entièrement d’un coup au client. Il est possible d’avoir des configurations plus avancées, qui permettent de ne pas inclure certaines parties du code dans cette fusion, comme certaines librairies volumineuses par exemple. Ce code ne sera alors téléchargé que s’il est vraiment utilisé par le client. On parle alors de chargement différé, ou Lazy Loading.
Traitement global
Une fois les fusions réalisées, il est possible d’effectuer une seconde passe d’opérations sur les fichiers obtenus.
En Node.js, c’est à ce stade que l’on réalise par exemple la minification du code. La minification consiste à réduire au minimum la syntaxe des fichiers, en renommant notamment les noms de variables, fonction et classe le plus synthétiquement possible, même si cela rend le code très peu lisible.
La minification peut être plus efficace si l’ensemble du code est présent dans un fichier unique. C’est pourquoi il est plus intéressant de minifier le code à ce niveau que lors du traitement des fichiers un à un.
Regroupement des fichiers de sortie
Une fois toutes les opérations effectuées, le bundler dépose le ou les fichiers résultant dans un dossier spécifique. Ce fichier contient le résultat de la compilation.
Il arrive également que l’on demande au bundler de copier des fichiers additionnels pour les inclure dans ce dossier, sans avoir à les modifier. On parle alors de fichiers statiques.
Webpack
Installation
Le bundler Webpack est une librairie Node.js. Vous pouvez donc l’ajouter à votre projet comme n’importe quel module :
jsx
npm install --save-dev webpackINFO
👌 L’option --save-dev permet d’ajouter le module comme dépendance de développement. Cela indique au projet Node.js que ce module ne sera pas utile au projet une fois en production.
Configuration
Il est possible d’utiliser Webpack en ligne de commande pour compiler un projet. Mais généralement on préférera définir la configuration de notre compilation dans un fichier JS, puis indiquer à Webpack de se référer à ce fichier pour la configuration.
Conventionnellement, on définit la configuration de Webpack dans un fichier webpack.config.js à la racine de notre projet Node.js. Le fichier doit réaliser un export defaut d’un objet contenant l’ensemble de la configuration.
Voici un exemple de fichier de configuration :
jsx
const UglifyJSPlugin = require('uglifyjs-webpack-plugin')
module.exports = {
entry: {
index: ["./src/index.js"],
},
output: {
path: path.join(__dirname, "dist"),
filename: "bundle.js",
publicPath: "/",
},
resolve: {
alias: {
'@img': path.resolve('./public/assets/picture')
}
},
module: {
rules: [
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
},
plugins: [
new UglifyJSPlugin()
]
};Cette configuration présente seulement quelques éléments basiques d’une configuration.
Le champ entry renseigne les informations sur l’emplacement du fichier d’entrée de notre application, ici son chemin.
Le champ output renseigne le chemin du dossier où placer les documents une fois compilés et le nom du fichier regroupant le code Javascript.
Le champ resolve vous permet d’ajouter des règles d’interprétation de votre code Javascript. C’est ici que vous pouvez indiquer les extensions potentielles des fichiers dont vous indiquez le chemin sans spécifier l’extension. C’est aussi là que vous pouvez définir des alias, comme ici où l’on indique qu’à la mention “@img”, il faudra faire correspondre le chemin vers le dossier contenant les images du projet, à savoir “./public/assets/picture”.
Le champ module permet de spécifier nos loaders, qui sont les opérations à réaliser sur les différents fichiers du projet, un à un. Le champ test permet de sélectionner les fichiers en fonction de leur nom grâce à une expression régulière. Le champ exclude permet de ne pas considérer certains dossiers. On peut ensuite dans le champ loader spécifier le loader que l’on souhaite appeler sur ces fichiers, et éventuellement fournir des options au loader.
WARNING
⚠️ Certains loaders sont natifs à la librairie Webpack, d’autres nécessitent que vous les installiez avec NPM, comme le loader de Babel.js avec : npm install -D babel-loader @babel/core @babel/preset-env webpack
Le champ plugins enfin renseigne sur les opérations effectuées une fois les fichiers fusionnés. Ici on fait appel au plugin natif uglifyjs-webpack-plugin qui s’occupe de minifier le code Javascript du projet.
L’exemple présenté ici ne décrit que les configurations essentielles qu’il est possible d’effectuer avec Webpack. Vos projets nécessiteront souvent une configuration plus complète que celle-ci. La documentation Webpack est richement détaillée pour vous permettre de comprendre facilement comment aller plus loin dans la configuration du bundler.
Exécution
On peut ensuite exécuter le module Webpack en faisant depuis la racine de notre projet la commande ./node_modules/.bin/webpack . Webpack va alors automatiquement récupérer notre configuration et compiler notre projet.
Pour éviter d’avoir à exécuter cette commande un peu complexe. Vous pouvez ajouter des scripts dans le fichier package.json :
json
"scripts": {
"build": "webpack"
}Cela vous permettra ensuite, depuis la racine de votre projet, de faire simplement la commande npm run build pour lancer la compilation de votre projet.
Webpack et Framework Frontend
La plupart des frameworks front-end modernes sur Node.js proposent de configurer eux-mêmes le bundler Webpack à la création d’un nouveau projet. Cela vous évite ainsi dans la plupart des cas d’avoir à y toucher.
Si vous souhaitez tout de même personnaliser le comportement de Webpack au sein du framework, vous ne pourrez pas nécessairement passer par un fichier webpack.config.js à la racine. Il faudra regarder dans la documentation de votre framework les moyens donnés pour surcharger la configuration par défaut. Exemple pour le framework Vue.js.
Aller plus loin
- Sur Webpack :
- Sur la configuration Webpack dans Vue.js : la documentation

