Inovia Blog

L’actualité des technologies innovantes

React ou l’art de la composition

Par Kadda SAHNINE

Publié le | 28 janvier 2016 |

Logo React


Eric Clemmons a publié sur Medium un billet d’humeur remarqué, intitulé JavaScript Fatigue, une lourde charge contre React et l’écosystème JavaScript.
Eric y déplore à juste titre la pénibilité d’initialiser un projet React, l’usage frénétique de boilerplates ou de générateurs de codes, la pléthore d’outils et d’API JavaScript en compétition les uns avec les autres, etc.

Tout cela est vrai. L’honnête développeur ou l’évangéliste JavaScript ne peuvent qu’acquiescer mais le réquisitoire contre React me paraît très injuste.
Pour appuyer cette conviction, je vous propose de découvrir les qualités de React à travers le développement d’un widget affichant les prochains passages du métro parisien dans une station donnée :

Widget RATP

Le rendu du widget est issu du code JSX ci-dessous :

On notera que le composant expose les propriétés suivantes :

  • type : le type de réseau (métro, rer, etc.)
  • line : le numéro de ligne
  • station : l’identifiant de la station
  • direction : l’identifiant de la direction

Le code source du widget est disponible sur mon dépôt GitHub.
Les données du trafic proviennent de l’excellente API REST de Pierre Grimaud

Mise en oeuvre pour les impatients

Si vous souhaitez tester localement ce projet, installez préalablement npm / node puis suivez le mode opératoire suivant :

git clone https://github.com/ksahnine/ratp-widget-react.git
cd ratp-widget-react
npm install
npm run dev
open http://localhost:8080
Note : je vous conseille d’utiliser npm/node à travers le gestionnaire de version nvm.

La structure du projet est la suivante :

├── app
│   ├── components
│   │   ├── RatpHeader.jsx
│   │   ├── RatpStatusBar.jsx
│   │   ├── RatpWidget.jsx
│   │   └── css
│   │       └── ratp-widget.css
│   └── main.js
├── build
│   └── index.html
├── package.json
└── webpack.config.js

React : en bref

React est une librairie JavaScript orientée composant développée par Facebook, permettant de construire des interfaces utilisateur modulaires et très performantes.
Elle jouit depuis plus d’un an d’une popularité grandissante, à telle enseigne que certains développeurs Angular envisagent sérieusement une conversion.
React encourage le développement d’interfaces utilisateur par composition, c’est-à-dire par assemblage de composants graphiques réutilisables pouvant eux mêmes être issus d’une composition. Ce modèle de conception est en train d’émerger dans le monde du développement des interfaces web et constitue une avancée majeure dans l’effort d’une plus grande standardisation des développements.

React présente au moins 3 avantages comparatifs :

  • la performance grâce au concept de DOM virtuel
  • l’approche composants
  • la possibilité de construire des applications universelles

Le DOM virtuel

Manipuler un arbre DOM directement via son API est compliqué. Suivre ses changements d’état est encore plus difficile et nécessite de développer des stratégies qui sont loin d’être neutres vis-à-vis des performances.
Angular 1 par exemple utilise la technique du dirty checking. Tous les objets du modèles sont surveillés en permanence, y compris ceux ne changeant jamais d’état.

Angular DOM

C’est une des raisons pour lesquelles Angular 1 pose des problèmes de performances, bien perceptibles sur des supports mobiles comme les tablettes.

React quant à lui implémente le concept de DOM virtuel, une structure de données JavaScript représentant l’arbre DOM dans un état donné. Lorsque le modèle de données évolue, React génère un nouveau DOM virtuel dans son intégralité, reflétant le nouvel état du modèle.

React DOM virtuel

React calcule ensuite le différentiel entre les 2 DOM virtuels et applique les modifications minimales à l’arbre DOM. Cette phase très performante est appelée réconciliation.

Orienté composant

Le principe de séparation des concepts (separation of concerns) appliqué au développement web consiste traditionnellement à séparer le code HTML, CSS et JavaScript. C’est un principe de division technologique.

Mais ce n’est pas la seule façon d’interpréter ce principe. L’approche de React est radicalement différente car elle consiste à développer des composants unitaires où template et logique de présentation sont encapsulés dans une même unité de développement en JavaScript.
Examinons cet exemple de composant React implémentant une fiche :

Ce qui peut décontenancer de prime abord, c’est l’impression de mélanger JavaScript, HTML et même CSS. Or ce n’est qu’une vue de l’esprit car tout est écrit en JavaScript, le code JSX n’étant que du sucre syntaxique pour rendre le code plus lisible.
Dans cette approche, le principe de séparation des responsabilités est plutôt fonctionnel, chaque composant remplissant une fonction élémentaire et exposant une interface bien définie (propriétés).

Applications universelles (ou isomorphiques)

Angular a popularisé le développement d’applications SPA (Single Page Application). Néanmoins, en plus de poser des problèmes de performance (l’ancien client web de Twitter en est un exemple fameux), cette architecture présente également l’inconvénient de rendre les sites non indexables par les moteurs de recherche.
React fonctionne aussi bien sur un navigateur (SPA) que sur un serveur (node.JS) ouvrant ainsi la voie au développement d’applications dites universelles (ou isomorphiques), c’est-à-dire pouvant s’exécuter de manière concomitante du côté client ou du côté serveur.

Pourquoi je crois en React

Au delà des qualités propres à React, je crois en la viabilité de cette librairie du seul fait de la pérennité du language JavaScript, quasiment la seule compétence requise pour développer avec React, alors que le développement d’une vue avec Angular ou tout autre framework MVC nécessite l’assimilation de nombreuses spécificités syntaxiques, par ailleurs changeantes au fil des versions (ex. ng-repeat Angular 1 devient ngFor sous Angular 2).
En outre, alors qu’Angular est plutôt centré sur HTML, React est totalement centré sur JavaScript (ES2015/ES6), incomparablement plus puissant que HTML.

Même si React est un projet encore jeune et que son écosystème fait penser à de la lave en fusion, je ne saurais trop vous conseiller de prendre ce projet au sérieux, ne serait-ce que pour pratiquer ECMAScript 6 et adopter un angle de développement tout à fait nouveau dans le monde des interfaces web.

Initialisation d’un projet : le trio React - Babel - webpack

C’est incontestablement la tâche la plus pénible. Elle a de quoi irriter beaucoup de développeurs tant elle peut être complexe, obscure et chronophage.
Nous utiliserons le gestionnaire de paquets npm pour installer les nombreux modules constituant la chaîne de développement et dont voici le descripteur package.json :

Essayons de démystifier tout cela. Les principaux composants utilisés dans la chaîne de développement sont :

React

Le projet utilise React 0.14.6, la version la plus récente publiée à ce jour.
Depuis la version 0.14, React a été découpé en deux paquets npm :

  • react : le coeur de la librairie React
  • react-dom : ce module est utilisé pour générer/synchroniser l’arbre DOM lors du rendu des composants React. L’opération peut se faire côté client (ReactDOM.render()) ou côté serveur (ReactDOMServer.renderToString()), ouvrant ainsi la voie au développement d’applications isomorphiques

L’idée de ce redécoupage est de mutualiser le code de React, ayant vocation à être multiplateforme (web ou mobile via React Native).

Babel

Même s’il est techniquement possible de développer un projet React en ECMAScript 5 (la version de JavaScript supportée par tous les navigateurs actuels), on tirera un énorme profit d’ECMAScript 6 tant les innovations de cette version simplifieront le développement.
On utilisera Babel 6.0, un transcompilateur JavaScript (ou compilateur source à source) transformant le code source ES6 en ES5.
Dans sa 6ème version, le code de Babel a été profondément réorganisé en plugins indépendants afin d’en faire une plateforme modulaire et ouverte vers d’autres outils ou langages.
Dans notre cas, nous utiliserons les modules :

  • babel-core : l’API node.JS de Babel
  • babel-loader : ce module est utilisé par webpack pour automatiser la transcompilation du code via Babel
  • babel-preset-es2015 : ensemble de plugins Babel (d’où le préfixe preset) pour le support ECMAScript 6.
  • babel-preset-react : ensemble de plugins Babel pour le support de React (la syntaxe JSX essentiellement).
  • babel-runtime : helpers et polyfills Babel

L’activation des presets babel-preset-es2015 et babel-preset-react nécessite de définir le fichier de configuration .babelrc à la racine du projet et avec le contenu suivant :

{
  "presets": ["es2015", "react"]
}

Webpack

Webpack est à la fois un gestionnaire de tâche et un assembleur de modules. Grâce à webpack, le code du projet peut être organisé en modules autonomes sans se soucier des problématiques de dépendances ou de packaging. On utilisera les packages npm webpack et webpack-dev-server.
Dans notre cas d’utilisation, webpack coordonne :

  • la transformation du code JSX en JavaScript
  • la transcompilation du code source ES6 en ES5
  • l’assemblage du projet et de ses dépendances en une seule unité de déploiement (dans notre cas, le fichier JavaScript bundle.js)

Webpack

Le module webpack-dev-server implémente un serveur HTTP local avec rechargement à chaud en cas de modification du code source.

Enfin, on utilisera également les 2 plugins webpack suivants :

  • css-loader :ce plugin permet “d’importer” une feuille de style CSS depuis un composant React
  • style-loader : ce plugin insère dynamiquement le code CSS d’une feuille de style importée dans une balise HTML style
Note : On aurait pu se passer de ces 2 plugins dans le cadre de notre projet.

Les règles d’assemblage du projet sont définies dans le fichier de configuration webpack.config.js situé à la racine du projet :

  • entry définit le point d’entrée de l’application (fichier app/main.js).
  • output définit le fichier résultant de l’assemblage des composants et de leurs dépendances (fichier build/bundle.js).
  • resolve.extensions contient un tableau des extensions identifiant les modules à assembler.
  • module.loaders contient 2 chargeurs, l’un traitant des imports CSS et l’autre du code JSX ainsi que de la transformation ES6/ES5 des fichiers contenus dans le répertoire app

L’application SPA est chargée dans le navigateur via le fichier statique build/index.html depuis un serveur web (ou plus exactement webpack-dev-server en mode développement) :

Développement du widget RatpWidget

Décomposition

Le schéma ci-dessous décrit un choix de décomposition du widget (RatpWidget) avec la hiérarchie de composants suivante :

  • RatpHeader : un cartouche affichant le logo de la ligne de métro, la station et la destination
  • RatpStatusBar : un message décrivant l’état du trafic sur la ligne

Décomposition du widget

RatpStatusBar

Le composant RatpStatusBar est très simple. Il affiche l’état du réseau défini par la propriété message du composant :

RatpStatusBar

Il est implémenté sous la forme d’un module ES6 afin de pouvoir être importé :

  • la directive import charge la librairie React.
  • le mot clé ES6 export permet d’exporter le composant au reste de l’application sous la forme d’un module.
  • l’API React.createClass() permet de définir un nouveau composant React.
  • la méthode render() implémente le rendu du composant, décrit par la syntaxe JSX. React invoque cette méthode dès que l’état ou une propriété du composant est modifié.
  • on notera à la ligne 11 que la propriété message du composant est utilisé pour afficher l’état du trafic ({this.props.message}).

RatpHeader

Passons au composant RatpHeader affichant le cartouche dont voici un exemple d’utilisation :

RatpHeader

Sa structure n’est pas très différente de celle du composant RatpStatusBar :

On remarquera à la ligne 7 que l’URL des icônes est dynamique et s’appuie sur de très pratiques template Strings, une fonctionnalité d’ES6.

RatpWidget

Analyson le code du composant (RatpWidget.jsx) :

  • 3 méthodes de l’API React sont utilisées :
    • getInitialState() retourne l’état initial du composant. Dans la terminologie React, un état (this.state) est un objet JavaScript accessible en lecture écriture et modélisant un ensemble de propriétés internes au composant.
    • render() implémente le rendu du composant. On notera l’utilisation des sous composants RatpHeader et RatpStatusBar
    • componentDidMount() est une méthode invoquée à l’initialisation du widget, juste avant l’appel à la méthode render(). Dans le cas d’espèce, on récupère les données du trafic via un appel AJAX.
  • Ligne 18 : la méthode fetchData récupère les données du trafic et met à jour l’état du widget (setState()).
    Note : l’architecture Flux (et en particulier le framework Redux) est un modèle de développement beaucoup plus adaptée à une application web.
  • Ligne 58 : les données sont mise à jour sur le clic du bouton de rafraichissement (onClick={this.fetchData}). Ainsi, l’état du composant est mis à jour et React invoque la méthode render() retournant le nouveau rendu.

Point d’entrée de l’application

Passons à la phase finale. Le point d’entrée de l’application est implémenté dans le fichier app/main.js :

On notera en particulier :

  • ligne 3 : import du composant React RatpWidget
  • ligne 8 : l’API RenderDOM.render() génère le rendu du composant RatpWidget (1er paramètre) dans l’arbre DOM (à l’intérieur du conteneur div passé dans le 2nd paramètre).

L’assemblage et la publication du projet est effectué par webpack via la commande npm run dev :

Abonnez vous !
  • RSS
  • Yahoo
  • Netvibes

Suivez l'auteur sur Twitter !
  • Suivre sur Twitter

A propos de l'auteur

Kadda SAHNINE
Architecte technique Java EE
#JavaEE #Linux #vim addict #OpenSoftware #OpenHardware
Voir le profil de Kadda Sahnine sur LinkedIn

Flux RSS

Rechercher

Administration