Chapitre 1 — Au commencement était le Web
Au début, il n’y avait rien. Puis du navigateur naquit soudain JavaScript. Les débuts de son existence n’ont pas été simples : d’abord cantonné au second rôle, haï et conspué, il a eu une enfance difficile jusqu’à ce qu’on le redécouvre avec les nouveaux usages d’un Web participatif et plus seulement consultatif. Historiquement, la vie et les rebondissements de JavaScript sont étroitement liés à la consommation du Web : d’un modèle documentaliste, les usages ont glissé vers un aspect plus communautaire (les blogs, les commentaires…) avant l’avènement du Web as a platform, où nos applications reposent désormais sur toute la stack technique Web.
Cette dernière révolution du WaaP nous impose à nous, développeurs, de revoir beaucoup de choses sur nos façons de penser, de procéder. Nous concevions des sites Web : des pages, parfois avec des interactions utilisateur, dans lesquelles nous faisions voyager les visiteurs au gré d’une navigation. Maintenant, nous développons aussi des Web apps : des applications beaucoup plus proches des modèles d’apps desktop traditionnelles, où les interactions de l’utilisateur forment la seule et unique notion d’interactions. Plus de page ni de navigation, nous devons rafraîchir et traiter des composants au sein d’un ensemble, plutôt que l’inverse. Les technos restent les mêmes, les paradigmes changent radicalement.
Face à cette révolution, pudiquement cachée sous le nom de Single Page App, il a fallu s’adapter. Parce que si JavaScript est puissant [1] , il n’en reste pas moins qu’un langage et sans une bonne boite à outils, on est vite pris au dépourvu. On a donc vu fleurir les Backbone, Angular, Ember, Batman et leurs dérivés (Chaplin, Marionette, Thorax, etc.). Des cadriciels [2] comme on en avait déjà ailleurs (Rails, Django, Symfony…), qui nous ont permis de structurer nos applications, le plus souvent en suivant des patterns éprouvés, dont la fameuse architecture Modèle-Vue-*. L’ennui avec cette architecture [3] dans son approche JavaScript, c’est :
- qu’elle n’offre pas nécessairement de base une structure où elle passe correctement à l’échelle, sur d’importants volumes, comme des applications offrant plusieurs dizaines d’interactions et de composants dans une même interface [4] ;
- qu’elle se contente bien souvent de laisser le développeur se débrouiller pour gérer la partie Vue [5].
Face à la complexité montante de nos Web apps, il était temps de passer à autre-chose…
Chapitre 2 — React : Back to the 80’s
Dans le flux des nouveaux outils se déversant quotidiennement dans nos timelines Twitter, React tient le haut du pavé depuis plusieurs mois, par son approche vue comme novatrice, et des patterns que nous n’avions encore jamais exploité dans notre écosystème Web. C’est la patate chaude, le truc que tout développeur front se doit d’avoir au moins essayé, sinon de l’utiliser sur ses projets, parfois ad nauseam [6].
Sous le capot, on trouve des concepts ingénieux : votre interface (la fameuse brique Vue) se découpe finalement en une multitude de petits composants. Chacun d’entre eux :
- possède un état, local (c’est-à-dire stocké dans le composant de vue) et temporaire, capable de décrire son composant à un instant T ;
- décrit (dans une syntaxe spécifique : JSX) le rendu attendu en fonction de son état (une représentation DOM) ainsi que les évènements auxquels il doit réagir [7] ;
- est taillé sur-mesure et expose aux autres composants un moyen simple de s’intégrer dans un ensemble plus vaste.
Sur le papier, c’est génial : on développe de petites briques, qui sont intelligemment autonomes, et qu’on assemble entre-elles pour former de plus vastes ensembles. Mon petit cœur d’enfant fou de Lego® en est ivre de joie.
L’autre côté furieusement malin, c’est la notion de virtual-rendering : au lieu de rendre un composant à chaque modification de son état (et donc flinguer le DOM en provoquant des reflows/repaints à gogo), celui-ci va simuler le rendu DOM attendu, le comparer à l’existant, et n’appliquer que les petites modifications nécessaires pour rester performant. Sur le papier, cette approche est sans faille ; dans les faits, il est parfois préférable de traiter certains rendus à la main, les algorithmes en charge de l’optimisation pouvant introduire des biais de performance. Dans tous les cas, il est toujours important de se rappeler que la magie ne reste que de la magie.
Se pose un problème maintenant : si chaque composant est local et autonome, comment les faire communiquer entre-eux ? Là encore, Facebook [8] offre une réponse : Flux. Flux, c’est l’architecture de communication sous-jacente à React. C’est un concept plus qu’une techno, et ça ressemble à ça :
Un cycle de vie unidirectionnel, dans lequel des actions sont poussées vers des dispatchers qui actualisent des stores qui modifient des états locaux de composants (et je vous passe les étapes intermédiaires). Et c’est là que le bât blesse : Flux est un concept sans doute prometteur, mais dont les implémentations de qualités, performantes et peu complexes, tardent à se faire connaître [9].
En clair, React seul n’est que l’arbre qui cache la forêt : c’est la techno suffisamment mûrie en interne par Facebook pour être mise en avant, mais elle n’est qu’un morceau de l’équation globale du Graal de l’application isomorphe [10]. React n’est que la Vue / le côté widget de l’application, et nécessite une structure en arrière-plan pour en tirer tout le bénéfice (Flux, Relay, GraphQL, etc.). Sa véritable révolution, c’est le virtual-rendering [11]. Même dans son approche « penser l’UI en petits composants atomiques », il n’apporte pas de réelle révolution. WebComponents est à l’origine, sur le Web, du design de composants, et React ne fait que reprendre ce concept pour l’encapsuler dans une logique de framework tout prêt où tout est (parfois trop) mélangé.
Ça vous plaît comment idée ? Fantastique, vous êtes prêt pour devenir développeur sur Windows 3.1 ! Parce que tous ces concepts, s’ils sont neufs sur le Web, ne sont pas nouveaux. Ils datent des premières interfaces et, sous Windows, portent le doux nom de WndProc
, le processus interne chargé d’actualiser l’état de chaque composant. Un excellent article paru il y a peu sous le titre the more things change explique parfaitement en quoi React n’est pas révolutionnaire et, même s’il est intéressant, ne constitue qu’une régression dans le domaine de l’architecture d’application.
Chapitre 3 — Soyez Fonctionnels…
L’une des plus grosses limites, à mon sens, dans la structure des applications React (c’est-à-dire avec tout l’attirail des briques proposées par Facebook), c’est que si elles sont nouvelles sur le Web, elles appliquent de vieux patterns à un langage qui est bien plus souple et plus puissant que ceux disponibles lorsque ces architectures ont été conçues. JavaScript est un langage à part, à la fois prototypé (au contraire des classes traditionnelles de la P.O.O., donc favorable à la composition), event-driven (basé sur une logique d’évènements), et fonctionnel (j’y reviens juste après, patience). Ces trois aspects nous permettent, ensemble, d’aller bien plus loin que ce que propose React.
Les plus avisés auront noté que je prends le contre-pied du fonctionnement affiché de React et ses createClass
en mettant en avant la composition plutôt que l’héritage. Toute la flexibilité de JavaScript et toute sa modernité pourrait se résumer à cette approche. Dans How to use classes and sleep at night, Dan Abramov se montre particulièrement lucide sur les limites des classes et de l’héritage, et de l’urgence à revoir nos modes de constructions. Mattias P. Johansson dans funfunfunction s’attaque à Composition over inheritance qui, comme toujours [12], est un modèle de clarté qui vous convaincra de l’importance de changer nos approches.
La programmation fonctionnelle, elle, est l’atout principal. Concrètement, il s’agit de concevoir des structures algorithmiques non-plus basées sur la donnée — j’ai une variable dont je traite et modifie le contenu, et qui vit tout au long du cycle de vie de mon application —, mais sur des évaluations fonctionnelles — ma source de donnée est pipée au travers d’une suite de fonctions qui vont émettre une nouvelle donnée en transformant la source. Les notions clés sont que :
- on ne fait qu’évaluer une suite de fonctions les unes derrière les autres (comme un pipe Unix) ;
- les données sont immutable : la source n’est jamais modifiée, chaque fonction évaluée ne fait que retourner une nouvelle source de données qui est passée au traitement suivant.
JavaScript, s’il n’est pas un langage purement fonctionnel à la base, offre des compétences fonctionnelles que vous connaissez déjà : map
, reduce
, filter
…
Pourquoi est-ce que cette approche est intéressante ? Parce que sa structure facilite sa relecture et sa maintenance ; que son aspect stateless rend l’ensemble plus cohérent à un instant T, et que sa notion de fonction plutôt que de donnée rend chaque état immutable, ce qui limite largement les effets de bord d’une variable modifiée par erreur.
Attendez deux secondes : immutable, cohérent, maintenable, structuré… Ça ne correspondrait pas à une vision logique de définition d’une vue d’interface, ça ?
Alors ça peut sembler compliqué au premier abord. Probablement, ça le sera quand vous vous y mettrez, parce que ça nécessite de concevoir sa structure différemment. Mais ça n’est pas plus compliqué qu’autre chose [13]. De plus, vous le pratiquez déjà si vous avez utilisé map
, reduce
, ou un grand nombre des méthodes d’underscore ou lodash. Et puis, implicitement, vous pratiquez déjà depuis plus de 10 ans une structure similaire avec les chaînages jQuery, transposer ça à toute une structure logique n’est pas difficile.
Chapitre 4 — … Réagissez !
Pourquoi passé-je du coq à l’âne avec React puis le Functionnal Programming ? Parce qu’un des enfants de la programmation fonctionnelle s’appelle Functionnal Reactive Programming, et que c’est exactement ce qu’on cherche à faire dans nos applis depuis des années.
Le FRP tire parti des aspects stateless et immutable du functionnal programming pour apporter la réactivité à vos composants. Dans cette approche, vous ne maintenez plus d’état pour un composant, vous ne faites que définir des flux d’informations observables. Ce peut être des propriétés, ou des évènements. Un peu comme le fait de vous tenir sur vos deux jambes : vous êtes dans un état d’équilibre incertain permanent. Un coup de vent (un évènement) va vous déséquilibrer. Votre cerveau va réagir en analysant cette information et rétablir votre équilibre pour éviter la chute en modifiant votre position. La modification de votre position va entraîner à son tour d’autres changements, et ainsi de suite [14].
Le FRP tire pleinement parti de la puissance de JavaScript parce qu’il est :
- totalement event-driven : vous ne gérez que des éléments de type
observables
pour déclencher des actions ; - totalement asynchrone : ce sont des flux d’évènements aléatoires imprévisibles dans le temps qui entraînent des actions ;
- totalement stateless : vous ne stockez plus d’état, vous vous contentez d’observer l’état d’une propriété à un instant T.
L’ajustement de ces concepts en fait un candidat idéal à l’élection du pattern d’interface réellement réactive. Vous ne poussez plus aucune info : vos composants observent ce qui les intéresse chez d’autres composants (ou en interne) pour s’adapter. Une forme de pub-sub intelligent, presque magique.
Si JavaScript en est capable, il faut bien évidemment lui fournir la boite à outils qui permettra d’appliquer un tel concept. Les librairies actuelles s’appellent Bacon.js, RxJS ou encore Kefir. Elle peuvent se coupler à des systèmes plus haut-niveau pour offrir un fonctionnement « à la React » [15], comme Cycle.js, Riot.js ou Paperclip.js, qui sont le plus souvent des moteurs de rendus DOM intelligents (avec du cache, du virtual-rendering, etc.) et qui s’adaptent en souplesse à des composants réactifs.
Alors, React or not React ?
Soyons bien d’accord, React est réellement nouveau sur le Web et dans notre façon d’aborder les interfaces. Nous absorbons de grands changements depuis quelques années avec le Web-as-a-platform, il est normal d’essayer de trouver des réponses. Mais au-delà de la promesse initiale, il faut garder à l’esprit que :
- React n’est pas Reactive (au sens entendu dans Reactive Programming) ;
- il offre une excellente abstraction à l’UI et nous pousse à penser nos vues différemment ;
- il nécessite une structure sous-jacente solide ;
- il a le mérite de lisser la courbe d’apprentissage (le fameux learn once, code everywhere) qui permet de développer dans un environnement commun des interfaces très diverses (depuis le mobile natif, à la console, en passant par le Web), au moins pour le socle de base, puisque même avec une base commune, développer pour le Web et en natif restent deux expériences différentes ; disons que vous partez au moins sur un environnement connu.
Malgré tout, dans un contexte de Web app totalement moderne, il n’est pas forcément la solution de choix, ni ne mérite d’être déployé aveuglément sous prétexte de son buzz ambiant. Comme jQuery à son époque, il est une solution temporaire à un état de transition dans nos concepts et nos approches, mais ne constitue pas la réponse ultime.
Je nuancerai plutôt en proposant que :
- pour des sites « traditionnels » (j’y inclus les plate-formes e-commerce, même complexes) avec une structure de navigation par page, un bon vieux jQuery fait largement l’affaire, si tant est que vous ayez encore besoin d’un support de vieux navigateurs. Au mieux, du VanillaJS reste votre meilleur allié ;
- pour des Web apps simples, un framework MV* à l’ancienne [16] ou une approche composant (type Riot.js voire Web Components) est souvent largement suffisant ;
- pour concevoir des applications plus complexes, cross-platforms, parfois isomorphe, React reste un bon choix et une transition douce ;
- sur des apps à forte interaction (ou des jeux), le passage au FRP apporte un indéniable gain pour gagner en souplesse et en flexibilité.
Ce n’est que le début…
Je vous l’ai spoilé en début d’article, nous sommes loin d’en avoir fini. Nous abordons la transition vers des interfaces nettement plus complexes et modernes, et React n’est qu’une étape. D’autres solutions plus matures, plus solides, plus souples se profilent déjà et méritent tout notre intérêt.
Si vous avez décidé de vous mettre au Reactive, regardez React, c’est une bonne école, une première étape. Mais ne perdez pas de vue que de nombreuses possibilités différentes, parfois traditionnelles, existent ; et que quand on n’a qu’un marteau, tout ressemble à un clou.
Vos commentaires
# Stefan Le 13 novembre 2015 à 09:46
Merci pour cet article complet et très intéressant
# Boris Le 13 novembre 2015 à 10:23
Un point de vue très intéressant, merci beaucoup, ça me fait réfléchir... React a également un autre avantage, celui de répondre à l’équation économique du système avec à la fois React Native qui offre la possibilité d’une montée en compétence simultanée mobile et Web (si elle n’est pas un mythe) et le support de Facebook qui est une assurance de pérennité (relative, hein, mais pour au moins 3-4 ans) pour de nombreux éditeurs en recherche d’une solution SPA vers laquelle se tourner.
Pour ma part, j’aurais préférer travailler avec des Web Components qui répondent de manière standard à la nécessité d’avoir des composants à états. Mais bon, puisqu’il faut aller plus vite que la musique, allons-y :)
# MoOx Le 13 novembre 2015 à 10:31
Il aurait été intéressant d’indiquer quels sont justement les solutions "plus matures, plus solides, plus souples [qui] se profilent déjà et méritent tout notre intérêt".
# Matthias Dugué Le 13 novembre 2015 à 12:58
Boris : pour React Native, on me faisait remarquer à l’écriture de cet article (merci David :)) que si la promesse est belle, il n’en reste pas moins que la courbe d’apprentissage n’est pas lissée : les paradigmes et les patterns (d’interface notamment) sur Mobile sont quand même très différents du Web, et même si la couche de base (React) est la même, l’usage risque d’êetre assez différent malgré tout. Donc j’attends de voir si le futur de React Native est aussi merveilleux que ce qu’on nous vend :P.
D’ailleurs, le support de Facebook est à la fois ce qui m’intéresse et ce qui m’inquiète : la puissane politique liée à un écosystème monoculturel est nocif, particulièrement sur le Web (mais je sais que tu sais :P). L’article de PandaStrike sur le sujet est intéressant.
Et pour les WebComponents, je vais totalement dans le même sens. Peut-être que React ne sera que le tremplin vers l’adoption massive d’un standard. Je l’espère en tous cas :).
MoOx : Je faisais référence aux libs FRP notamment et aux outils qui commencent à les embarquer (Cycle.js, par exemple). Les solutions matûres, au sens large du dév, existent depuis longtemps dans d’autres langages et sur d’autres plateformes. Je pense sincèrement que React ouvre la voie à adapter tous ces patterns sur le Web. J’ai juste dans l’idée que c’est loin d’être fini et que le futur des apps sur le Web va bien au-delà de React :).
# Samuel Bouchet Le 13 novembre 2015 à 15:16
Merci Matthias, super article !
Pour moi la force de React n’est pas tant dans son DOM virtuel (d’autres librairies le font très bien, tu les a nommé : RiotJS, CycleJS, etc.), mais plutôt dans son approche axée autour d’un flux de donnée unidirectionnel (Intéractions -> Actions -> Données -> Rendu) qui fait qu’à tout moment le rendu de la vue est prédictible humainement pour peu qu’on fasse de sa vue une fonction pure (avec bonus de performance si on utilise des données immuables).
En bref : le DOM virtuel fait le café pour les perfs, mais c’est l’architecture autour qui créer une valeur ajoutée en permettant à l’appli d’évoluer sans explosion de complexité, et tout en restant prédictible (donc maintenable).
Je te rejoins sur le fait qu’on a pas forcément besoin de React pour mettre en place une architecture « reactive-like » ou complètement réactive, mais en attendant que le concept se généralise j’apprécie la robustesse de React.
Le grand retour de la programmation fonctionnelle est-il pour bientôt ? ;)
# Pierre Goiffon Le 18 novembre 2015 à 09:16
Bonjour, merci pour votre article, très intéressant !
Juste une petite remarque : les "notes de bas de page" très nombreuses compliquent grandement la lecture... Et ce contenu n’a pas trop nécessité à être sous cette forme ? Ajouter des tooltips ou simplement des parenthèses ?
# Gaëtan Le 16 mai 2016 à 09:41
Vraiment très intéressant et très agréable à la lecture. Merci !...
# Hervé Le 18 août 2016 à 17:33
Merci pour cet article (que je viens lire suite à la relance sur Twitter ;)) qui présente clairement les concepts intéressants de React.
En revanche j’ai l’impression que l’inconvénient principal de React dans la pratique c’est, comme le dit cet article intitulé "Javascript Fatigue", la multiplication des dépendances, non ?
# Matthias Dugué Le 23 août 2016 à 22:58
Bonjour Hervé,
merci pour le retour (et merci aux copains d’OpenWeb pour les relances sur Twitter :P).
Effectivement, React (et d’autres) sont au cœur de la tempête "Javascript Fatigue" qui fait rage depuis quelques mois. Le problème n’est, à mon avis, pas tant la mutliplication des dépendance (encore que, mais c’est un autre problème je dirai), que la question de la pérénnité de ces frameworks. Dans les langages plus établis, on trouve des solution robustes qui font, sinon l’unanimité, du moins assez fortement consensus dans les communautés de développeurs.
Pour JavaScript, le problème est différents : les navigateurs se livrent une guerre acharnée pour obtenir des moteurs d’éxecution de plus en plus rapides ; le langage est reparti à évoluer dans le bon sens (ES6) ; les nouvelles fonctionnalités navigateurs offrent un plein potentiel. Dans l’effervescence des briques bas-niveau de la conception de nos webapps, il devient compliqué de mettre au point de façon durable un framework au-dessus de fondations aussi mouvantes. On se retrouve donc avec chaque semaine une tripotée de nouveaux outils tous plus malins, plus puissants, plus efficaces que ceux présents encore la veille (et qui faisaient déjà le café et revenir l’être aimé).
Bref, choisir une solution JS frontend robuste, établie, et pérenne, à l’heure actuelle, c’est utopique. Dès que le socle (l’évolution des navigateurs) se stabilisera, les choses se tasseront probablement. Mais, bonne nouvelle, ce n’est probablement pas pour tout de suite ! Il ne nous reste plus qu’à prendre notre mal en patience, ne pas se jeter sur la nouveauté à chaque nouveau projet, et essayer de tirer parti de l’expérience engrangée sur les projets précédents. Surtout, je crois qu’il faut tenir pour acquis que quoi qu’il arrive, un code ne restera jamais en prod en front sans aucune évolution pendant plus de 18 mois (sur un projet vivant, j’entends). Partant de là, travailler avec des solutions jetables dans le temps devient plus acceptable :).
n.b : je recommande à toutes fins utiles la merveilleuse conférence de l’ami Thibault Jouannic sur la veille techno pour les vieux croutons qui illustre finalement bien qu’il n’est pas nécessaire de coller à la nouveauté pour faire de la qualité.
Vos commentaires
Suivre les commentaires : |