Choisir le bon framework frontend peut en effet s'avérer difficile, en particulier dans un paysage qui évolue rapidement.
L'objectif de cet article est de vous aider à sélectionner l'architecture appropriée en fournissant une vue d'ensemble de l'évolution des frameworks front.
Pour bien comprendre les défis actuels auxquels les frameworks front répondent, il est essentiel d'explorer les différentes architectures front existantes et d'acquérir une compréhension de leurs avantages et inconvénients respectifs.
En examinant ces architectures, vous pourrez développer une base solide pour évaluer et sélectionner le framework le plus adapté à votre projet.
- Tout d'abord, nous allons nous pencher sur les différentes architectures front afin d'acquérir une compréhension globale de l'évolution des frameworks frontaux.
- Ensuite, nous nous intéresserons aux techniques de rendu employées par les frameworks de rendu côté serveur (SSR).
- Enfin, nous explorerons les différentes techniques d'hydratation utilisées pour améliorer l'interactivité des applications web.
Multi-page Apps (MPA)
L'application multi-pages (MPA) était l'architecture initiale utilisée lorsque les capacités des navigateurs étaient limitées. Dans cette architecture, la majorité des processus dynamiques se déroulent en arrière-plan.
Voici comment fonctionne la navigation dans une application MPA :
- Le client navigue sur le site web (par exemple, en cliquant sur un lien) et le navigateur fournit un retour visuel (par exemple, en affichant un loader dans l'onglet).
- Le routage a lieu au niveau du backend.
- Les données sont extraites de la base de données.
- Le backend génère le HTML, qui est ensuite renvoyé au client.
- Le navigateur affiche le HTML.
Voici un autre exemple qui implique une redirection après la soumission d'un formulaire (un schéma bien connu utilisé pour empêcher la re-soumission d'un formulaire lorsque la page est rafraîchie) :
- Le client soumet un formulaire, ce qui donne lieu à un retour sur l'interface utilisateur.
- Le routage a lieu et le backend gère les données.
- Le backend demande au navigateur de rediriger l'utilisateur.
- Le navigateur redirige automatiquement l'utilisateur vers la nouvelle page, fournissant à nouveau un retour sur l'interface utilisateur.
- Le backend génère ensuite le code HTML de la nouvelle page.
- Enfin, le navigateur rend le code HTML.
Quels sont les avantages d'une telle architecture ?
- Elle est simple à comprendre puisque toute la logique réside dans le backend, ce qui élimine toute complexité supplémentaire.
- Le retour visuel est géré par le navigateur.
Quels sont les inconvénients ?
- Elle implique des rafraîchissements complets de la page, ce qui nécessite de recharger à chaque fois les fichiers du frontend.
- Certaines tâches peuvent être difficiles à accomplir, comme la gestion du focus ou la position de défilement après la soumission d'un formulaire.
- Certaines tâches ne sont pas pratiques, par exemple le rafraîchissement complet de la page à chaque fois qu'un élément est ajouté à la liste des favoris.
- Certaines tâches sont tout simplement impossibles, comme les transitions de page animées.
- La gestion de l'état peut être délicate, impliquant l'utilisation de cookies et d'autres techniques.
- Le retour visuel de l'interface utilisateur est limité et n'est pas étroitement associé à l'élément avec lequel l'utilisateur a interagi.
En raison de ces limitations, d'autres architectures ont été introduites.
Progressively enhanced multi-page Apps (PEMPA)
L'amélioration progressive est le concept selon lequel nos applications web doivent être fonctionnelles et accessibles à tous les navigateurs web, puis utiliser les capacités supplémentaires du navigateur pour améliorer l'expérience de l'utilisateur (par exemple, en utilisant JS et AJAX pour améliorer l'expérience utilisateur).
Voici ce qui se passe lorsque l'utilisateur charge le site web pour la première fois :
- Le client charge le site web, à partir d'une recherche Google, par exemple.
- Comme pour l'AMP, le backend génère le code HTML et le renvoie au client.
- Le navigateur rend le HTML et le JavaScript est chargé.
Examinons maintenant le cas où nous utilisons JavaScript pour exploiter l'historique du navigateur lorsque la page est déjà chargée :
- Le client navigue sur le site web.
- JavaScript intercepte le comportement par défaut du navigateur, ajoute un élément à l'historique de navigation du navigateur et modifie l'URL du navigateur (aucun retour d'information du navigateur).
- Les données sont extraites du backend à l'aide d'AJAX.
- Du côté du backend, le composant de vue n'est plus présent et les données sont généralement renvoyées au frontend au format JSON.
- Le frontend reçoit les données et rend la nouvelle interface utilisateur.
Quels sont donc les avantages ?
- L'expérience de l'utilisateur est considérablement améliorée (c'est l'objectif principal).
Et quels sont les inconvénients ?
- Plus d'aspects à gérer manuellement (par exemple, la re-soumission des formulaires, la gestion des erreurs, les loaders, etc.).
- Augmentation de la quantité de code à la fois sur le frontend et le backend, ce qui peut conduire à plus de bogues.
- Duplication du code entre le backend et le frontend (la même interface utilisateur doit être disponible des deux côtés en raison de l'amélioration progressive).
- Utilisation d'un code impératif (avec ou sans jQuery) qui peut être difficile à suivre.
- Si un changement est effectué d'un côté du réseau (backend/frontend), l'autre côté doit également être mis à jour.
Bien que l'expérience utilisateur soit améliorée, cela à clairement un coût, c'est pourquoi de nouvelles architectures ont été introduites.
Single page applications (SPA)
Pour résoudre le problème de duplication de code tout en maintenant une expérience utilisateur améliorée, la solution consiste à séparer et à supprimer le code de l'interface utilisateur du backend. C'est l'idée centrale des applications à page unique (SPA).
Voici ce qui se passe lorsque l'utilisateur charge initialement le site web :
- Le client charge le site web.
- Similaire à MPA et PEMPA, le backend génère le HTML et l'envoie au client. Cependant, à ce stade, le HTML est principalement vide.
- Le navigateur affiche le HTML presque vide et le JavaScript est chargé.
- Selon l'URL du navigateur, le JavaScript récupère les données du backend.
- Le backend envoie les données au format JSON.
- JavaScript génère l'interface utilisateur en fonction de l'URL.
Après le chargement initial, la navigation suit un schéma similaire à celui de la situation PEMPA :
Les avantages des SPA sont les suivants :
- Amélioration continue de l'expérience utilisateur.
- Amélioration de l'expérience des développeurs.
- Élimination de la duplication de code.
- Utilisation d'un code déclaratif grâce à du JSX ou des approches similaires.
- Utilisation d'outils modernes.
Cependant, il y a quelques inconvénients :
- Problèmes de référencement (SEO).
- Taille des bundles plus importante.
- Temps de chargement initial plus long et problèmes potentiels de cascade (attente du chargement des composants avant de charger les données).
- Performances réduites sur les appareils moins puissants.
- Gestion complexe de l'état.
Maintenant, le problème de référencement (et parfois de performances) doit être résolu, ce qui nous amène à la prochaine architecture.
Server-side rendered single page application (SSRSPA)
Pour résoudre le problème de référencement, il est nécessaire de rendre l'application sur le serveur lors du chargement initial.
Dans ce cas, l'application se charge de manière similaire au scénario PEMPA :
Cependant, lorsque le JavaScript est chargé, nous utilisons le terme "hydratation" (“hydration” en Anglais) pour décrire le processus de rendu de la page interactive.
Cela est similaire au rendu qui se produit côté backend, où l'arborescence des composants est transformée en HTML.
Dans le cas de l’hydratation, le DOM virtuel généré est utilisé pour ajouter des écouteurs d'événements aux éléments DOM existants.
La navigation peut suivre le même schéma que le scénario MPA ou le même schéma que le scénario PEMPA/SPA, en fonction de la technique de rendu utilisée (expliquée dans la section suivante).
Les avantages de cette architecture sont les suivants :
- Amélioration de l'expérience utilisateur.
- Amélioration de l'expérience des développeurs, y compris l'élimination de la duplication, le code déclaratif et l'amélioration des outils.
- Plus de problèmes de référencement (bien que cela ne soit peut-être pas considéré comme un avantage par certains).
- Amélioration potentielle des performances, selon la technique de rendu utilisée.
Cependant, il y a quelques inconvénients :
- La gestion de l'état reste complexe.
- La capacité à exécuter du JavaScript côté serveur est nécessaire.
Techniques de rendu
Les techniques suivantes sont toutes des techniques de rendu côté serveur (SSR) qui impliquent le traitement des composants côté backend pour générer du HTML dans la réponse HTTP et résoudre les problèmes de référencement. Elles diffèrent du rendu côté client (CSR) utilisé par l'architecture SPA, qui ne rend le contenu que côté client.
Dynamic rendering (DR)
Dans cette technique, la page est générée de manière dynamique sur le serveur. Cette technique était principalement utilisée pour résoudre les problèmes de référencement de l'architecture SPA en rendant les composants du framework frontend sur le serveur et en envoyant le résultat généré au client.
Cependant, en termes de performances, elle n'est pas nécessairement supérieure au rendu côté client (CSR) utilisé par l'architecture SPA (sans utiliser de mise en cache côté serveur).
Le temps nécessaire pour rendre l'interface utilisateur est similaire, qu'il s'agisse du rendu sur le serveur pour le DR ou du rendu côté frontend pour le CSR.
En fait, cela peut même être pire en raison de la phase d'hydratation (détaillée dans la section suivante).
Le rendu dynamique est disponible dans des frameworks tels que Next.js (React), Gatsby (React), Nuxt (Vue), et autres.
Comme mentionné précédemment, par rapport au CSR (SPA), l'avantages du rendu dynamique est le suivant :
- Pas de problèmes de référencement.
Cependant, il y a quelques inconvénients :
- La page est générée côté backend, nécessitant Node.js et consommant plus de ressources serveur que le CSR.
- La mise en cache est nécessaire pour améliorer les performances.
- La gestion de l'invalidation du cache en fonction des modifications côté backend peut être complexe.
- La page doit être hydratée côté client pour l'interactivité.
- Le contenu généré côté serveur et côté client lors de l'hydratation peut différer (par exemple, lors de l'utilisation de JavaScript pour le responsive).
Static site generation (SSG)
Dans la technique de génération de site statique (SSG), les pages sont générées à l'avance lors de la phase de compilation.
Les pages sont ensuite stockées sous forme de fichiers HTML statiques et servies en tant que contenu statique.
Cette technique est également prise en charge dans des frameworks tels que Next.js (React), Gatsby (React), Nuxt (Vue), et autres.
Le SSG offre les avantages suivants :
- Pas de problèmes de référencement.
- Charge minimale du serveur.
- Chargement rapide des pages.
Cependant, il y a quelques inconvénients :
- Les pages sont générées pendant la phase de compilation et restent entièrement statiques jusqu'à la prochaine compilation.
- Le processus de compilation peut prendre du temps.
- Toutes les pages doivent être générées pendant la compilation.
- La page nécessite toujours une hydratation côté client pour devenir interactive.
Incremental static regeneration (ISR)
La régénération statique incrémentielle (ISR) est une technique de rendu qui combine les avantages du SSR et du SSG.
Comme pour le SSG, les pages sont générées à l'avance lors de la phase de compilation.
Cependant, il existe deux options pour gérer la mise à jour des pages :
1. Time-based ISR : vous pouvez spécifier un intervalle de temps pendant lequel la page sera mise en cache.
- Initialement, la page statique est servie pour toutes les requêtes.
- Lorsque le temps spécifié expire, le framework génère une nouvelle version de la page en arrière-plan.
- Les requêtes ultérieures utiliseront alors la page mise à jour.
2. On-demand ISR : votre système back-office peut informer le framework front chaque fois qu'il y a des modifications, déclenchant ainsi la régénération de pages spécifiques. Cela permet des mises à jour dynamiques sans dépendre d'un intervalle de temps prédéterminé.
Next.js (React) prend en charge à la fois la revalidation basée sur le temps (Time-based ISR) et la revalidation à la demande (On-demand ISR), tandis que Nuxt (Vue) prend actuellement uniquement en charge la revalidation basée sur le temps (On-demand ISR).
Les avantages de l'ISR incluent :
- Compatibilité avec le référencement (SEO).
- Charge réduite du serveur par rapport au SSR.
- Chargement rapide des pages.
- Capacité à avoir un contenu dynamique.
Cependant, il y a quelques considérations :
- La revalidation basée sur le temps peut entraîner des problèmes de cache potentiels, car un contenu plus ancien peut être servi jusqu'à l'expiration du cache.
- L'implémentation d'une logique de revalidation entre le front-office et le back-office est nécessaire pour la revalidation à la demande.
- L'hydratation de la page est toujours nécessaire pour l'interactivité.
Jamstack
Selon Jamstack.org :
Jamstack est une approche architecturale qui sépare la couche d'expérience web des données et de la logique métier, améliorant la flexibilité, la scalabilité, les performances et la maintenabilité.
Les principes fondamentaux de Jamstack comprennent :
- Séparer la compilation et l'hébergement.
- Séparer le frontend et le backend.
- Utiliser des API plutôt que des bases de données (exposer les données via des API, utiliser des outils externes, etc.).
Générer du code HTML pré-généré, amélioré avec du JavaScript.
En pratique, la configuration Jamstack implique les étapes suivantes :
- Génération du balisage statique pré-rendu à l'aide d'un générateur de site statique.
- Hébergement des ressources statiques sur un CDN (réseau de diffusion de contenu).
- Amélioration du balisage statique avec JavaScript à l'aide d'un framework frontend.
- Intégration avec des API backend pour du contenu dynamique.
- Stockage de l'ensemble du projet dans un système de contrôle de version tel que Git.
- Utilisation de builds automatisés pour simplifier le processus de déploiement.
Les avantages de l'utilisation de Jamstack incluent : - Compatibilité avec le référencement (SEO), permettant une meilleure visibilité sur les moteurs de recherche.
- Réduction de la charge du serveur grâce à la distribution des ressources statiques via un CDN.
- Temps de chargement rapide des pages pour une expérience utilisateur améliorée.
- Possibilité d'intégrer du contenu dynamique via des API backend.
Cependant, il y a quelques considérations : - La gestion du contenu et les mises à jour doivent être gérées via des systèmes de contrôle de version tels que Git.
- Pour le contenu stocké dans une base de données, une logique personnalisée est nécessaire pour générer les données à l'intérieur de Git (et peut-être déclencher une nouvelle compilation).
- L'hydratation de la page est toujours nécessaire pour l'interactivité côté client.
Résumé
Nous pouvons résumer les différentes techniques de rendu dans le tableau suivant :
Techniques d'hydratation
Lors de l'utilisation du Rendu côté serveur (SSR - Server-Side Rendering), le serveur envoie le HTML au navigateur, qui l’affiche ensuite. Cependant, tant que le JavaScript n'est pas chargé, l'utilisateur ne pourra pas interagir avec la page.
L'objectif de l'hydratation est de permettre l'interactivité sur la page en attachant des écouteurs d'événements à la structure du DOM générée par le serveur.
Les différentes techniques ont pour but d'améliorer les performances en rendant la page interactive pour l'utilisateur le plus rapidement possible après avoir reçu la réponse du serveur.
Routes statiques
En fait, s'il n'y a pas d'interactions sur certaines pages, il n'est pas nécessaire de les hydrater.
Dans de tels cas, il n'est également pas nécessaire d'envoyer du JavaScript au client.
Cela peut sembler évident, mais tous les frameworks n'ont pas la capacité d’envoyer zéro JavaScript.
Dans de tels cas, nous obtenons une architecture d'Application à pages multiples (MPA - Multi-Page Application), car sans JavaScript, il n'y aura pas de routage côté client.
Cette technique est disponible dans les frameworks suivants : Remix, Sveltekit et SolidStart.
Hydratation classique
Lors de l'utilisation de l'hydratation classique, la structure de l'arbre des composants est générée sur le serveur pour créer le HTML.
Cependant, côté client, la structure de l'arbre est générée à nouveau afin que la bibliothèque puisse comparer le HTML existant et lier les événements aux éléments appropriés.
La première vidéo de ce lien montre ce qui se passe dans le navigateur lors de l'hydratation classique.
C'est essentiellement ce qui se produit lors de l'utilisation de Next.js avec l'ancien routage “Pages Router”.
Hydratation progressive (ou hydratation paresseuse)
L'objectif de l'hydratation progressive est d'utiliser des composants dynamiques pour différer le chargement des composants non urgents et prioriser l'hydratation des composants urgents.
Cette technique nécessite une implémentation manuelle par le développeur, qui décide quels composants doivent être chargés de manière paresseuse et comment ils doivent être chargés.
Les deuxième et troisième vidéos de ce lien démontrent les différences entre l'hydratation classique et l'hydratation progressive.
Les exigences techniques pour la mise en œuvre de l'hydratation progressive sont les suivantes :
- Permettre l'utilisation de SSR pour tous les composants.
- Prendre en charge la découpe du code en composants ou en fragments individuels.
- Prendre en charge l'hydratation côté client de ces fragments dans une séquence définie par le développeur.
- Ne bloque pas la saisie utilisateur sur les fragments déjà hydratés.
- Permettre l'utilisation d'un indicateur de chargement pour les fragments avec une hydratation différée.
Hydratation sélective
L'idée derrière l'hydratation sélective est de renvoyer une réponse incomplète depuis le serveur.
En streamant la réponse, nous pouvons recevoir une réponse partielle du backend même si certaines requêtes n'ont pas encore été terminées.
Grâce à cette technique, nous pouvons commencer à charger le JavaScript même lorsque la page est incomplète afin de commencer l'hydratation sur les éléments présents.
Lorsque nous recevons la réponse de la requête, nous pouvons l'envoyer de nouveau au navigateur, qui remplacera l'interface utilisateur incomplète et procédera ensuite à l'hydratation du nouveau fragment de page, comme indiqué dans les vidéos de cette page.
Cette fonctionnalité a été introduite dans React 18 avec renderToPipeableStream et implémentée dans Next.js.
Hydratation partielle (architecture des îlots)
L'hydratation partielle est une sorte de version mise à jour des Routes statiques.
Le but des îlots est d'hydrater au niveau du composant, plutôt que de la page entière.
Cette technique peut être utilisée lorsque seuls certains composants doivent être interactifs.
Tout ce qui se trouve en dehors de ces îlots/composants ne changera jamais et ne sera pas interactif.
Cette technique est utilisée par Astro, Marko, Fresh et 11ty.
Elle est également disponible dans Next.js ou Gatsby en utilisant les composants serveur React.
Résumable application
Ce concept est utilisé par Qwik.
L'idée derrière une application “resumable” est de sérialiser l'état de l'application sur le serveur, puis de reprendre à partir de ce point côté client.
Même les gestionnaires d'événements sont sérialisés, comme dans cet exemple :
<button on:click="q-671f8656.js#s_D04jAYuCnhM[0 1]">click me</button>
Qwik utilise également largement le chargement différé pour ne charger le JavaScript que lorsque cela est nécessaire, par exemple lorsque l'on clique sur le bouton dans l'exemple ci-dessus.
Cela permet d'avoir une application qui envoie un minimum de JavaScript intégré lors du chargement initial, tout en restant hautement interactive.
Résumé
Toutes ces techniques donnent des résultats de performance différents et peuvent avoir certaines limitations. Cependant, il n'est pas nécessaire de les comparer de manière exhaustive.
Les principaux frameworks tiennent déjà compte de ces techniques et évoluent continuellement pour offrir de nouvelles améliorations de performance.
Dans la plupart des cas, si votre application est lente, ce n'est probablement pas dû à la technique d'hydratation utilisée.
Par conséquent, l'hydratation ne devrait probablement pas poser de problème si vous suivez la documentation du framework, mais il est tout de même utile de connaître ces techniques.
Conclusion
Avons-nous besoin d'un Framework Frontend ?
La décision d'utiliser un framework frontend dépend des exigences spécifiques du projet.
Voici les avantages de l'utilisation d'un framework frontend :
- Maintenabilité et évolutivité
- Expérience utilisateur rapide et agréable
- Approche frontend standardisée
- Prototypage et développement rapides
D'un autre côté, voici quelques inconvénients à prendre en compte :
- Complexité pour les projets simples
- Difficulté d'intégration avec les outils backend
- Nature dogmatique des frameworks
- Nécessite formation et familiarité
En résumé, il est recommandé d'utiliser un framework frontend lorsque vous avez une interface frontend complexe avec un nombre important d'interactions utilisateur.
Headless ou non ?
L'architecture headless n'est pas toujours la solution.
Si vous avez uniquement besoin de certaines interactions sur certaines pages, vous pouvez utiliser des frameworks frontend pour ces composants spécifiques sans pour autant utiliser le framework pour l'ensemble de la page.
Voici quelques considérations pour différents frameworks :
- React peut être utilisé pour générer uniquement des composants spécifiques, mais toujours côté client et donc pas SEO-friendly (uniquement pour les parties rendues avec React).
- Vue.js peut être utilisé en mode Progressive Enhancement, car il est basé sur le HTML servi par le serveur (et donc est quant à lui SEO-friendly).
- Angular ne convient pas bien à ce scénario.
- Certains frameworks, comme Alpine.js, sont spécifiquement conçus pour ce cas d'utilisation.
Référencement ou non ?
Si le référencement n'est pas une exigence, les frameworks de rendu côté client (CSR) sont généralement suffisants.
Voici quelques exemples de scénarios où le référencement n'est pas critique :
- Back-offices ou panneaux d'administration
- Applications nécessitant une authentification
- Applications sans contenu indexable (par exemple : Excalidraw )
Choix de la technique de rendu
- Si le contenu est dans le code source, le JamStack peut être utilisé.
- Si vous construisez un site web avec des modifications peu fréquentes, la génération de site statique (SSG) peut être utilisée.
- Si vous pouvez implémenter une logique de mise à jour du contenu, la régénération statique progressive (ISR) avec revalidation à la demande peut être utilisée.
- Si vous souhaitez utiliser une mise en cache basée sur le temps, l'ISR avec revalidation basée sur le temps peut être utilisée.
- Sinon, le rendu côté serveur (SSR) est recommandé.
Récapitulatif
Pour résumer, la décision d'utiliser un framework frontend, une architecture headless, les considérations de référencement et la technique de rendu doivent être basées sur les exigences spécifiques du projet.
Le schéma ci-dessous fournit une représentation visuelle qui peut aider à choisir l'architecture appropriée avec la technique de rendu correspondante :
Veuillez noter que ce schéma sert de ligne directrice utile, mais il peut y avoir certains cas particuliers où il peut ne pas s'appliquer pleinement. Il est important de prendre en compte les caractéristiques uniques et les besoins de votre projet lors de la prise de ces décisions.
Ressources
https://www.epicweb.dev/the-webs-next-transition
https://buchet.tech/blog/csr-ssr-ssg-isr-wtf
https://www.sitepen.com/blog/intro-to-html-first-frontend-frameworks
https://www.patterns.dev/
https://dev.to/this-is-learning/why-efficient-hydration-in-javascript-frameworks-is-so-challenging-1ca3