Perché abbandonare Webpack Encore
Webpack Encore è stato a lungo lo standard Symfony per il frontend. Ma sui miei progetti React la constatazione era sempre la stessa: avvio del dev server in 15-20 secondi, hot reload lento, configurazione verbosa. Vite risolve tutto questo — avvio quasi istantaneo grazie ai moduli ES nativi, HMR in pochi millisecondi e una configurazione minimale. Ecco come lo integro in modo pulito in un progetto Symfony.
L'installazione: pentatrion/vite-bundle
Il ponte tra Symfony e Vite è il bundle pentatrion/vite-bundle, che fornisce le funzioni Twig per iniettare gli asset — l'equivalente di ciò che faceva Encore.
composer require pentatrion/vite-bundle
npm install
La recipe Flex crea una cartella assets/ con un vite.config.js di base. Aggiungiamo React:
npm install react react-dom
npm install -D @vitejs/plugin-react
Il cuore del tema: gli entry point
Un entry point è un file JS/CSS che Vite compila come punto di ingresso indipendente. In un'architettura Symfony + isole React (quella che uso su baccalaureat.sn), ogni area interattiva ha il proprio entry point — non si carica tutto React su una pagina che non ne ha bisogno.
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import symfonyPlugin from 'vite-plugin-symfony';
export default defineConfig({
plugins: [
react(),
symfonyPlugin(),
],
build: {
rollupOptions: {
input: {
app: './assets/app.js', // JS global (Turbo, styles)
quiz: './assets/quiz/index.jsx', // ilot React : le quiz
chat: './assets/chat/index.jsx', // ilot React : l'assistant IA
},
},
},
});Tre entry point = tre bundle indipendenti. La pagina iniziale carica solo app, la pagina quiz carica app + quiz. Il guadagno in peso è immediato rispetto a un bundle monolitico.
Iniettare gli asset in Twig
Il bundle fornisce due funzioni Twig che sostituiscono encore_entry_script_tags:
{# templates/base.html.twig #}
{% block stylesheets %}
{{ vite_entry_link_tags('app') }}
{% endblock %}
{% block javascripts %}
{{ vite_entry_script_tags('app') }}
{% endblock %}{# templates/quiz/show.html.twig #}
{% block javascripts %}
{{ parent() }}
{{ vite_entry_script_tags('quiz') }}
{% endblock %}
<div id="quiz-root"
data-matiere="{{ matiere }}"
data-serie="{{ serie }}"></div>La magia del bundle: in dev, queste funzioni puntano al server Vite (localhost:5173) con HMR attivo; in prod, leggono il manifest del build e iniettano i file con hash. Lo stesso codice Twig funziona in entrambi gli ambienti.
Montare l'isola React
Ogni entry point React monta il proprio componente sulla sua div, leggendo le props dai data-attribute renderizzati da Twig:
// assets/quiz/index.jsx
import { createRoot } from 'react-dom/client';
import QuizApp from './QuizApp';
const el = document.getElementById('quiz-root');
if (el) {
createRoot(el).render(
<QuizApp
matiere={el.dataset.matiere}
serie={el.dataset.serie}
/>
);
}Il if (el) non è opzionale: l'entry point può essere caricato su una pagina in cui la div non esiste (cache Turbo, layout condiviso). Senza la guardia, è crash JS silenzioso garantito.
Il workflow dev
# Terminal 1 : Symfony
symfony serve
# Terminal 2 : Vite en mode dev (HMR)
npm run dev
Si naviga sul sito servito da Symfony, e ogni modifica di un componente React si riflette senza ricaricamento della pagina, stato conservato. È qui che Vite schiaccia Encore: il ciclo modifica → risultato visibile passa da diversi secondi all'istantaneo.
Il build di produzione
npm run build
# genere public/build/ avec les fichiers hashes + manifest.json
Sul mio VPS, il deploy concatena semplicemente:
git pull
composer install --no-dev --optimize-autoloader
npm ci && npm run build
php bin/console cache:clear --env=prod
Nessun Node in runtime: Vite serve solo al build, Apache serve file statici con hash e cache lunga. Il VPS fa girare solo PHP-FPM.
Cosa ne ricavo
- Un entry point per ogni isola React: si carica solo il JS necessario a ogni pagina.
- vite_entry_script_tags() gestisce da solo il passaggio dev (server Vite + HMR) / prod (manifest con hash).
- Le props passano da Twig a React tramite i data-attribute — semplice e senza API aggiuntive.
- Tenere sempre il if (el) prima del createRoot: un entry point può vivere su una pagina senza la sua div.
- In prod, Node non esiste più: Vite è uno strumento di build, non un server.