Introduction
Webpack 5 reste en 2026 l'outil de bundling le plus puissant pour les applications JavaScript modernes, surpassant Vite pour les projets complexes nécessitant une personnalisation fine. Contrairement aux bundlers zero-config, Webpack offre un contrôle granulaire sur le tree-shaking, le code splitting et les optimisations assets, essentiels pour des apps scalables comme celles avec micro-frontends ou PWAs.
Ce tutoriel intermédiaire vous guide pas à pas pour configurer un projet complet : transpilation ES6+ via Babel, gestion CSS/Sass, Hot Module Replacement (HMR), et builds production avec minification et chunking. Vous obtiendrez un setup fonctionnel, testé, prêt pour l'intégration CI/CD. Idéal pour les développeurs seniors gérant des monorepos ou des libs partagées, où la flexibilité prime sur la simplicité. À la fin, votre bundle sera optimisé pour Lighthouse 100/100.
Prérequis
- Node.js 18+ installé
- Connaissances en JavaScript ES6+, modules import/export
- npm ou yarn comme gestionnaire de paquets
- Éditeur de code (VS Code recommandé)
- Terminal Unix-like (WSL sur Windows)
Initialisation du projet
mkdir webpack-modern-project
cd webpack-modern-project
npm init -y
npm install --save-dev webpack@5 webpack-cli webpack-dev-server html-webpack-plugin mini-css-extract-plugin css-loader style-loader sass-loader sass babel-loader @babel/core @babel/preset-env
npm install lodashCette commande initialise un projet npm et installe Webpack 5 avec ses outils essentiels : CLI pour les scripts, dev-server pour HMR, plugins pour HTML/CSS, loaders pour CSS/Sass/Babel, et lodash comme dépendance pour tester l'import. Évitez les versions globales pour isoler les projets.
Scripts package.json
{
"name": "webpack-modern-project",
"version": "1.0.0",
"scripts": {
"dev": "webpack serve --mode development --open",
"build": "webpack --mode production",
"build:analyze": "webpack --mode production --profile --json > stats.json"
},
"devDependencies": {
"@babel/core": "^7.24.0",
"@babel/preset-env": "^7.24.0",
"babel-loader": "^9.1.3",
"css-loader": "^7.0.0",
"html-webpack-plugin": "^5.6.0",
"mini-css-extract-plugin": "^2.9.0",
"sass": "^1.77.0",
"sass-loader": "^14.0.0",
"style-loader": "^4.0.0",
"webpack": "^5.94.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.1.0"
},
"dependencies": {
"lodash": "^4.17.21"
}
}Les scripts définissent dev pour le serveur de développement avec auto-ouverture du navigateur, build pour production, et build:analyze pour l'analyse des bundles via webpack-bundle-analyzer (optionnel). Cela structure les workflows sans redondance.
Fichiers source de base
import _ from 'lodash';
import './styles.scss';
const message = `Webpack 5 fonctionne ! Taille lodash: ${_.size({key: 'value'})}`;
document.body.innerHTML = `<h1>${message}</h1><div class="box">Boîte stylée</div>`;
console.log('Projet prêt pour HMR');Ce fichier d'entrée importe lodash (tree-shakable), un SCSS, et manipule le DOM. Il démontre le bundling multi-ressources. Créez aussi src/styles.scss avec .box { background: blue; padding: 20px; color: white; } pour tester.
Configuration Webpack de base
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
clean: true
},
devServer: {
static: './dist',
port: 3000,
hot: true
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
title: 'Webpack 5 Moderne'
})
],
mode: 'development'
};Configuration initiale avec entry/output hashés pour cache-busting, nettoyage auto du dist, et devServer pour HMR/port 3000. HtmlWebpackPlugin injecte le bundle dans un template HTML. Créez src/index.html basique : .
Premier test en développement
Exécutez npm run dev : Webpack lance un serveur sur http://localhost:3000 avec HMR activé. Modifiez src/index.js ou SCSS : les changements s'appliquent instantanément sans reload complet, comme un "chaud" live-reload. Vérifiez la console pour confirmer lodash et les styles.
Ajout de Babel pour transpilation
{
"presets": [
["@babel/preset-env", {
"targets": {
"chrome": "58",
"firefox": "57",
"ie": "11",
"safari": "12"
},
"useBuiltIns": "usage",
"corejs": 3
}]
]
}"@babel/preset-env" transpile ES6+ vers des browsers legacy, avec polyfills on-demand via core-js. Targets définissent la compatibilité ; useBuiltIns: 'usage' optimise en important seulement les polyfills nécessaires, évitant un bundle gonflé.
Loaders Babel et CSS dans webpack.config
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
assetModuleFilename: 'assets/[hash][ext][query]',
clean: true
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.(scss|css)$/i,
use: [
process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
'css-loader',
'sass-loader'
]
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource'
}
]
},
devServer: {
static: './dist',
port: 3000,
hot: true
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
title: 'Webpack 5 Moderne'
}),
...(process.env.NODE_ENV === 'production' ? [new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})] : [])
],
mode: 'development'
};Règles loaders : Babel pour JS (exclut node_modules), CSS/Sass chain (style-loader dev, MiniCssExtract prod), asset/resource pour images (copie optimisée). Conditionnel sur NODE_ENV sépare dev/prod. Ajoutez une image dans src pour tester.
Optimisations avancées
Testez : Ajoutez du code async ES2022 dans index.js (ex: top-level await). Babel transpile, HMR persiste. Pour prod, npm run build génère dist/ avec CSS extrait, JS minifié (via TerserPlugin implicite), assets hashés.
Code splitting avec dynamic imports
import _ from 'lodash';
import './styles.scss';
const message = `Webpack 5 avec splitting ! Taille lodash: ${_.size({key: 'value'})}`;
document.body.innerHTML = `<h1>${message}</h1><div class="box">Cliquez pour lazy load</div><button id="load">Charger chunk</button>`;
const btn = document.getElementById('load');
btn.addEventListener('click', async () => {
const { lazyFunc } = await import('./lazy.js');
lazyFunc();
});
console.log('HMR prêt');Dynamic import crée un chunk séparé pour ./lazy.js (créez-le : export const lazyFunc = () => alert('Chunk chargé !');). Webpack split auto, prefetch/preload optionnels via magic comments. Idéal pour lazy-loading routes.
Configuration finale avec splitting et perf
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: {
main: './src/index.js'
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
assetModuleFilename: 'assets/[hash][ext][query]',
clean: true
},
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\\/]node_modules[\\\/]/,
name: 'vendors',
chunks: 'all',
priority: 20
}
}
}
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: 'babel-loader'
},
{
test: /\.(scss|css)$/i,
use: [
process.env.NODE_ENV === 'production' ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader',
options: { importLoaders: 1 }
},
'sass-loader'
]
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
generator: {
filename: 'assets/[hash][ext][query]'
}
}
]
},
devServer: {
static: './dist',
port: 3000,
hot: true,
open: true
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html',
title: 'Webpack 5 Moderne'
}),
...(process.env.NODE_ENV === 'production' ? [
new MiniCssExtractPlugin({
filename: '[name].[contenthash].css'
})
] : [])
],
mode: process.env.NODE_ENV || 'development'
};Optimisation splitChunks extrait vendors (lodash) en chunk partagé. ChunkFilename pour dynamic imports. CSS-loader importLoaders:1 applique Sass à @import. Perf : bundles <100kb gzippés, cache via hash.
Bonnes pratiques
- Séparez dev/prod : Utilisez
process.env.NODE_ENVpour conditionner loaders/plugins, minimisant le bundle dev. - Tree-shaking : Importez nommément (ex:
import { debounce } from 'lodash'), et activezsideEffects: falsedans package.json. - Cache agressif : Hashes sur tous outputs +
cache: { type: 'filesystem' }dans webpack.config pour builds 10x plus rapides. - Analyse bundles : Ajoutez webpack-bundle-analyzer pour visualiser tailles/chunks.
- Source maps :
devtool: 'source-map'en dev,'hidden-source-map'en prod.
Erreurs courantes à éviter
- Pas d'exclude node_modules sur babel-loader : bundle explose (GoT 10s+).
- Oubli clean: true : Fichiers orphelins polluent dist/ après rebuilds.
- Style-loader en prod : CSS inline gonfle JS ; utilisez toujours MiniCssExtractPlugin.
- Pas de splitChunks : Vendors dupliqués dans chaque chunk, +200kb/app.
Pour aller plus loin
- Documentation officielle : Webpack 5 Guide
- Avancé : Module Federation pour micro-frontends
- Outils : SpeedMeasurePlugin pour profiler builds