Webpack Encore : front-end moderne avec Symfony
Webpack Encore est la solution officielle de Symfony pour gerer les assets front-end - JavaScript, CSS, images et polices. C'est un wrapper autour de Webpack qui simplifie drastiquement la configuration tout en conservant toute la puissance de l'ecosysteme Webpack. Ce guide couvre l'installation, la configuration et les techniques avancees pour un front-end moderne avec Symfony 7.2.
Installation et setup initial
L'installation se fait en deux etapes : le bundle PHP et les dependances npm :
# Installation du bundle
composer require symfony/webpack-encore-bundle
# Installation des dependances npm
npm install
# Ou avec yarn
yarn install
Flex genere automatiquement la structure de fichiers :
assets/
app.js # Point d'entree principal
bootstrap.js # Initialisation Stimulus
styles/
app.css # Styles principaux
controllers/ # Controllers Stimulus
webpack.config.js # Configuration Encore
Configuration de webpack.config.js
Le fichier webpack.config.js est le coeur de la configuration. Encore expose une API fluide qui simplifie les options Webpack :
const Encore = require('@symfony/webpack-encore');
Encore
// Dossier de sortie (dans public/)
.setOutputPath('public/build/')
.setPublicPath('/build')
// Points d'entree
.addEntry('app', './assets/app.js')
.addEntry('admin', './assets/admin.js')
// Styles sans JavaScript associe
.addStyleEntry('print', './assets/styles/print.css')
// Runtime chunk unique (recommande)
.enableSingleRuntimeChunk()
// Nettoyage du dossier build a chaque compilation
.cleanupOutputBeforeBuild()
// Source maps en developpement
.enableSourceMaps(!Encore.isProduction())
// Versioning en production (hash dans les noms de fichiers)
.enableVersioning(Encore.isProduction())
// Configuration Babel pour la compatibilite navigateurs
.configureBabelPresetEnv((config) => {
config.useBuiltIns = 'usage';
config.corejs = '3.38';
})
;
module.exports = Encore.getWebpackConfig();
Entries : organiser vos points d'entree
Chaque addEntry() cree un point d'entree Webpack. Un entry produit un ou plusieurs fichiers JS et CSS :
// assets/app.js - Entry principal
import './styles/app.css';
import './bootstrap.js';
// Votre code applicatif
console.log('Application chargee');
// assets/admin.js - Entry admin (charge uniquement sur /admin)
import './styles/admin.css';
import './admin/dashboard.js';
import './admin/users.js';
Dans vos templates Twig, incluez les entries avec les fonctions Encore :
{# templates/base.html.twig #}
<!DOCTYPE html>
<html>
<head>
{% block stylesheets %}
{{ encore_entry_link_tags('app') }}
{% endblock %}
</head>
<body>
{% block body %}{% endblock %}
{% block javascripts %}
{{ encore_entry_script_tags('app') }}
{% endblock %}
</body>
</html>
{# templates/admin/base.html.twig #}
{% extends 'base.html.twig' %}
{% block stylesheets %}
{{ parent() }}
{{ encore_entry_link_tags('admin') }}
{% endblock %}
{% block javascripts %}
{{ parent() }}
{{ encore_entry_script_tags('admin') }}
{% endblock %}
Les fonctions encore_entry_link_tags et encore_entry_script_tags generent automatiquement les bonnes balises HTML avec le hash de version en production.
CSS, Sass et PostCSS
Encore supporte plusieurs preprocesseurs CSS. Activez-les dans la configuration :
Encore
// Sass/SCSS
.enableSassLoader()
// PostCSS (avec autoprefixer, tailwindcss, etc.)
.enablePostCssLoader()
// Les deux ensemble
.enableSassLoader()
.enablePostCssLoader()
;
Pour Sass, installez les dependances :
npm install sass sass-loader --save-dev
Pour PostCSS avec Tailwind CSS :
npm install postcss postcss-loader tailwindcss autoprefixer --save-dev
// postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
Exemple de structure Sass
// assets/styles/app.scss
@use 'variables';
@use 'base';
@use 'components/header';
@use 'components/footer';
@use 'components/card';
@use 'pages/home';
@use 'pages/product';
// assets/styles/_variables.scss
$primary: #2563eb;
$secondary: #7c3aed;
$font-family: 'Inter', system-ui, sans-serif;
$container-max-width: 1200px;
Asset versioning et cache busting
Le versioning ajoute un hash au nom des fichiers generes. Cela permet un cache agressif (1 an) car chaque modification produit un nouveau nom de fichier :
# Sans versioning
/build/app.js
/build/app.css
# Avec versioning (production)
/build/app.a1b2c3d4.js
/build/app.e5f6g7h8.css
Encore genere un fichier entrypoints.json qui mappe les noms logiques vers les fichiers physiques. Les fonctions Twig lisent ce fichier pour generer les bonnes URLs. Vous n'avez rien a gerer manuellement.
Dev server avec Hot Module Replacement
Le dev server offre une experience de developpement optimale avec le rechargement automatique :
# Compilation classique (watch mode)
npm run watch
# Dev server avec rechargement auto
npm run dev-server
# Dev server avec HMR (remplacement sans rechargement)
npm run dev-server -- --hot
Configuration avancee du dev server :
Encore
.configureDevServerOptions(options => {
options.server = {
type: 'https',
options: {
pfx: path.join(process.env.HOME, '.symfony5/certs/default.p12'),
}
};
options.allowedHosts = 'all';
})
;
Le HMR est particulierement utile pour le CSS : vos modifications de style sont appliquees instantanement sans perdre l'etat de la page.
Code splitting : optimiser le chargement
Le code splitting extrait les modules partages entre plusieurs entries pour eviter la duplication :
Encore
// Extraire les chunks communs
.splitEntryChunks()
// Configuration fine du splitting
.configureSplitChunks(splitChunks => {
splitChunks.minSize = 30000;
splitChunks.cacheGroups = {
vendors: {
test: /[\/]node_modules[\/]/,
name: 'vendors',
chunks: 'all',
priority: 10,
},
};
})
;
Avec splitEntryChunks(), si app.js et admin.js utilisent tous les deux lodash, celui-ci est extrait dans un chunk separe charge une seule fois.
Import dynamique et lazy loading
Pour les fonctionnalites chargees conditionnellement, utilisez les imports dynamiques :
// assets/app.js
// Ce module n'est charge que si l'element existe sur la page
const chartElement = document.getElementById('sales-chart');
if (chartElement) {
import('./components/SalesChart.js').then(({ default: SalesChart }) => {
new SalesChart(chartElement);
});
}
// Avec async/await
async function initEditor() {
const { Editor } = await import('./components/Editor.js');
return new Editor('#editor');
}
Webpack genere automatiquement des chunks separes pour chaque import dynamique. Le navigateur ne les telecharge que quand ils sont necessaires.
Gestion des images et polices
Encore gere les assets statiques (images, polices) et les copie dans le dossier build avec versioning :
// En CSS/Sass - les URLs sont resolues automatiquement
.hero {
background-image: url('../images/hero-bg.jpg');
}
@font-face {
font-family: 'CustomFont';
src: url('../fonts/CustomFont.woff2') format('woff2');
}
// En JavaScript
import heroImage from '../images/hero.jpg';
const img = document.createElement('img');
img.src = heroImage; // URL versionnee automatiquement
Pour copier des fichiers sans les traiter (favicons, fichiers statiques) :
Encore
.copyFiles({
from: './assets/images',
to: 'images/[path][name].[hash:8].[ext]',
pattern: /.(png|jpg|jpeg|gif|svg|webp)$/
})
;
Integration Stimulus Bridge
Le @symfony/stimulus-bridge connecte automatiquement les controllers Stimulus des bundles UX :
Encore
.enableStimulusBridge('./assets/controllers.json')
;
// assets/controllers.json (genere automatiquement par Flex)
{
"controllers": {
"@symfony/ux-chartjs": {
"chart": {
"enabled": true,
"fetch": "eager"
}
},
"@symfony/ux-live-component": {
"live": {
"enabled": true,
"fetch": "eager"
}
}
},
"entrypoints": []
}
Le bridge detecte les controllers exposes par les packages dans node_modules et les enregistre dans l'application Stimulus. Pas besoin d'import manuel. Pour en savoir plus sur Stimulus et les composants UX, consultez notre article sur Symfony UX en pratique.
Build de production
La commande de build production active automatiquement les optimisations :
# Build de production
npm run build
# Ce qui est active automatiquement :
# - Minification JS (Terser) et CSS
# - Tree-shaking (suppression du code non utilise)
# - Versioning (hash dans les noms de fichiers)
# - Source maps desactivees (sauf si forcees)
# - Optimisation des images
Pour analyser la composition de votre bundle :
# Generer un rapport d'analyse
npm run build -- --analyze
Ce rapport visuel montre la taille de chaque module et aide a identifier les dependances a optimiser. Pour l'integration dans un theme Bootstrap, consultez notre guide sur l'integration Bootstrap avec Symfony. Et si vous envisagez d'integrer React dans votre front-end Symfony, lisez notre article sur l'architecture Symfony + React.
Besoin d'une configuration front-end optimisee pour votre projet Symfony ? Consultez mes tarifs, decouvrez mes services, ou contactez-moi pour en discuter.
