Symfony à toute vitesse : Boostez votre développement avec Hotwire, ImportMaps et SymfonyUX

Vladislav Solntsev
ekino-france
6 min readOct 3, 2023

--

En juin dernier, une PR de Kevin Dunglas, core-team du framework Symfony et créateur du framework API Platform, est passée pratiquement inaperçue. Dans celle-ci, un nouveau component pour le moment expérimental est intégré à Symfony — l’AssetMapper. Celui-ci utilise la notion des import maps afin de se passer de Webpack Encore, yarn ou npm pour charger les assets JavaScript au sein d’une application Symfony — ce qui simplifie énormément la façon de les configurer et de les maintenir.

Ce component a été annoncé quelques mois plus tôt fin mars 2023, lors de la SymfonyLive à Paris, pendant laquelle pas une mais deux conférences ont touché le sujet de la place du front dans une application back. La première est celle de Kévin Dunglas citée plus haut, et la deuxième celle de Florent Destremau qui a présenté Hotwire — une approche alternative pour créer des SPA.

Nous allons voir pourquoi ces deux sujets sont liés au sein du framework Symfony, leur origine et la façon dont on peut l’utiliser dès aujourd’hui, ainsi que d’autres alternatives.

Commençons par Hotwire

Si on souhaite ajouter du contenu dynamique à une appli web, le premier réflexe serait d’utiliser du JavaScript, parfois là où on pourrait s’en passer. La mode des dernières années serait d’utiliser un “module bundler” comme Webpack, ce qui alourdit grandement un projet.
Et si on revenait aux sources pour utiliser le web comme on le faisait dans le passé, tel qu’il avait été conçu — seulement pour dynamiser nos pages web quand il y en a besoin ?

Même si l’idée était présente depuis longtemps, un des premiers à avoir proposé une solution pour faire des applications interactives sans JavaScript au grand public était Chris McCord, créateur du framework Phoenix pour le language Elixir, en présentant en 2019 LiveView. LiveView est une fonctionnalité d’Elixir qui permet des mises à jour en temps réel des pages web sans que ces pages ne se rechargement complètement, avec l’utilisation des connexions Websocket pour synchroniser l’état et les événements entre le serveur et le client.

Peu après en 2021, David Hansson, le créateur de Ruby on Rails annonçait Hotwire — “une approche alternative pour créer des applications Web modernes sans utiliser beaucoup de JavaScript en envoyant du HTML au lieu de JSON sur le réseau.”. Dans un article publié à l’occasion, il explique que l’équipe de Ruby on Rails a voulu adopter une philosophie voulant se détacher d’un JavaScript lourd pour faire des SPA avec les outils du navigateur, et ne pas utiliser un framework JavaScript complexe.
En résumé, avoir le moins de JavaScript possible tout en ayant le même rendu et le dynamisme d’un React, par exemple. Quand on regarde en détail, Hotwire est pour le moment basé sur deux briques principales — Stimulus et Turbo.

  • Turbo : permet de faire une SPA sans JavaScript. Il utilise les Turbo Frames, qui décomposent la page en plusieurs blocks avec leur propre contexte, qui peuvent être lazy-loaded. Quand on utilise une frame, seulement le contenu de cette frame change, comme sur une SPA. Le reste de la page reste le même, ce qui rend l’application plus réactive. Chaque frame a un contexte indépendant qui lui est propre. Turbo utilise également les Turbo Streams qui permettent de manipuler le DOM, par exemple d’ajouter ou de supprimer un élément de la page, via les WebSocket ou en réponse de soumissions de formulaires en n’utilisant que du HTML.
    Cela permet de créer par exemple une modale sans JavaScript très simplement, en faisant un turbo frame imbriqué pour un formulaire et gérer les erreurs ou la validation sans avoir besoin de recharger toute la page, ou bien de créer un simple loader.
    Mais que faire quand la manipulation du DOM n’est pas assez pour notre besoin ? Dans ce cas-là on va utiliser la deuxième brique de Hotwire Stimulus.
  • Stimulus : utile pour les cas où on ne peut pas se passer de JavaScript. C’est un petit framework JS pour le HTML, qui n’a pas vocation à remplacer complètement un frontend, mais seulement améliorer le HTML là où il le faut. Il utilise des objets JS appelés Controlleurs, qui relient le HTML au JavaScript.

Le but principal d’Hotwire est donc de remettre le JavaScript là où était sa place, dans le navigateur web et pas en ligne de commande ou côté serveur. C’est un retour à une approche plus traditionnelle du web — HTML qui structure, CSS qui décore et le JS qui améliore, enrichi quand c’est nécéssaire pour l’interactivité.

Et les import maps dans tout ça ?

Ceux qui utilisent Symfony ont l’habitude de charger leurs assets JS via l’intermédiaire de Webpack, en utilisant un gestionnaire de paquets comme NPM ou yarn. Il y a également besoin d’installer Node comme runtime JS, Encore pour Webpack et parfois même plus de dépendances.
Tout cela est assez lourd, et en théorie on pourrait faire plus simple d’autant plus que les navigateurs modernes ont tous les outils nécessaires pour y remédier.

Il y a notamment les ES modules, implémentés depuis 2015 qui ont depuis évolué, avec un système similaire aux namespaces en PHP. On peut dès à présent utiliser ces modules avec les navigateurs web modernes. Mais que faire avec les dépendances tierces ?

La solution est d’importer un module en utilisant une URL, relative ou absolue, à l’aide des import maps. C’est un standard HTML qui a commencé à être implémenté par les navigateurs courant 2021, qui se présente sous la forme suivante :

<script type=”importmap”>

Grâce à ce tag, il est possible de simplifier l’import des modules JavaScript, en précisant au navigateur la façon de résoudre les spécificateurs de ces modules, en créant une correspondance d’une instruction “import” et sa valeur correspondante.

Par exemple, au lieu d’avoir

import { name as circleName } from “https://example.com/shapes/circle.js"; 

<script type=”importmap”>
{
“imports”: {
“circle”: “https://example.com/shapes/circle.js"
}
}
</script>

On peut alors utiliser cette syntaxe dans nos fichiers JS :

import { name as circleName } from “circle”;

On peut donc utiliser ces avancées déjà présentes dans les navigateurs, sans avoir besoin de charger un framework JS, avec un mapping du nom du module avec l’url de ce module dans le CDN. Un autre avantage est la rapidité de chargement — vu que chaque module est séparé, si un d’eux seulement change, les autres restent dans le cache.

Concrètement, qu’en est-il côté Symfony ?

Comme nous avons déjà vu, l’atout principal d’Hotwire et de Symfony UX Turbo est le fait de ne pas avoir à configurer et maintenir tout un large framework front comme React ou Vue en plus de notre application Symfony et d’avoir besoin de développeurs front experts dans ces technologies. De plus, en chargeant un framework on récupère toute la complexité de celui-ci avec énormément de fichiers à charger, ce qui n’est pas le cas avec Hotwire.

En effet, Symfony UX utilise aussi des dépendances tierces NPM, qui ont besoin d’être chargées. A présent, grâce à ce nouveau composant, inspiré par rails/import-maps, on peut utiliser le même principe dans Symfony, avec tous les outils adaptés : un gestionnaire de paquets JS, une CLI qui ressemble à Composer avec des commandes pour installer et gérer les packages NPM, une conversion du format NPM en un module ESM (avec l’utilisation d’un CDN opensource, JSPM).
Il y a également la possibilité de ne pas dépendre des CDN et pouvoir télécharger les packages directement, ce qui est assez intéressant pour une utilisation en production.
De plus, pas de panique pour les anciennes versions de navigateurs - un polyfill existe et remplit son rôle pour la rétro compatibilité.
Comme toute technologie celle-ci a également ses limitations, si vous souhaitez coder en TypeScript, ou bien utiliser des fichiers .jsx de React par exemple, vous aurez toujours besoin de Webpack Encore — mais pour tous les besoins en JavaScript plus simples, l’AssetMapper fera grandement l’affaire.

Si le sujet vous a intéressé mais que Symfony ou même PHP n’est pas votre language de prédilection, d’autres alternatives existent, parmi lesquelles on peut citer :

  • HTMX (https://htmx.org/) pour travailler directement avec le HTML. Il ajoute des attributs spéciaux aux éléments HTML pour permettre les mises à jour dynamiques via des requêtes HTTP. HTMX est relativement simple à intégrer dans les projets existants et ne nécessite pas d’apprendre un nouveau langage ou un nouveau framework.
  • Django Sockpuppet (https://sockpuppet.argpar.se/) pour Python. Il permet de créer des applications Django interactives en utilisant des “puppets”, qui sont des composants réutilisables avec leur propre logique.
  • Phoenix LiveView (https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.html) pour Elexir, qu’on a déjà mentionné. Il gère les mises à jour dynamiques en utilisant un modèle basé sur des sockets.
  • Laravel Livewire (https://livewire.laravel.com/) comme alternative pour PHP sous Laravel, qui minimise la nécessité d’écrire du code JavaScript pour des interactions en temps réel.

--

--

Vladislav Solntsev
ekino-france

Web developper, currently at Bordeaux. Passionate about coding server-side business logic, Symfony & Drupal and all things about scripting & automation.