jQuery / Angular / ReactJS

Voir la vidéo

Aujourd'hui je vous propose de parler des différents Frameworks et librairies qu'il existe en JavaScript. Vous êtes plutôt nombreux à demander qu'est-ce que je pense de jQuery, d'AngularJS ou encore de React avec des questions qui ont plus ou moins de sens ("C'est quoi le mieux entre jQuery et AngularJS ?").

La véritable problématique que l'on rencontre aujourd'hui est que l'on est entouré d'une flopée de librairies et il est difficile de s'y retrouver. Cette abondance est encore plus problématique lorsque l'on débute, car on a tendance à attaquer des librairies sans comprendre totalement leur utilité / fonctionnement. L'idée n'est pas de faire une comparaison entre les différentes technologies, mais plutôt de vous expliquer pourquoi on en est là aujourd'hui.

JavaScript est un langage très vieux (à l'échelle d'internet) et qui est né avec Netscape Navigator 2.0 en 1995 et qui permet d'interagir avec la page Web. Au début, on l'utilisait de manière assez limitée pour afficher quelques petites alertes et contrôler les formulaires.

Autour de l'année 2006, le paysage a commencé à changer et de nouveaux navigateurs sont arrivés comme Mozilla Firefox ou Safari qui essayent de faire avancer le Web plus rapidement. Chacun proposant une implémentation du JavaScript différente, un code pouvait alors fonctionner sur un navigateur et planter complètement sur un autre (on connaît encore ce problème aujourd'hui, mais dans un moindre mesure). Du coup on se retrouvait toujours à écrire des conditions afin que notre code fonctionner partout, ce qui complique rapidement notre code.

Par exemple pour faire un "simple" appel Ajax :

var xhr;
try {
  xhr = new ActiveXObject('Msxml2.XMLHTTP');
} catch (e) {
  try {
    xhr = new ActiveXObject('Microsoft.XMLHTTP');
  } catch (e2) {
    try {
      xhr = new XMLHttpRequest();
    } catch (e3) {
      xhr = false;
    }
  }
}

xhr.onreadystatechange = function() {
  if (xhr.readyState == 4) {
    if (xhr.status == 200) {
      "ok";
    }
  }
};

xhr.open("GET", "data.xml", true);
xhr.send(null);

jQuery "write less, do more"

Ainsi, c'est autour de cette période-là que jQuery et d'autres outils comme Script.aculo.us, Mootools ou YUI ont commencé à voir le jour. Le principe de ces librairies était de combler ces problèmes de compatibilité et de rendre le JavaScript facile à écrire.

// Avec jQuery le code précédent ressemble à ça
$.get( "data.xml", function( data ) {
  "ok"
});

Plus simple non ? Du coup, ces librairies ont été adoptées de manière massive et c'est pour cela qu'aujourd'hui il est très difficile de trouver un plugin JavaScript qui n'utilise pas jQuery. Actuellement, jQuery est un petit peu moins utile qu'avant parce que les navigateurs ont évolués et les API JavaScript se sont simplifiées (querySelectorAll() permet de simplifier grandement la sélection d'éléments par rapport au bon vieux getElementByTagName()) mais reste pratique pour tout ce qui est animation et Ajax afin d'avoir un code plus concis et plus simple.

Grâce à cette simplification, on a commencé à écrire de plus en plus de JavaScript. À tel point que l'on a commencé à créer des applications qui dépendent totalement du JavaScript. C'est ainsi que sont nées les Singles Page Applications qui sont des applications Web basées sur une seule page HTML intitiale, qui va ensuite évoluer au fil des interactions.

  • L'utilisateur interagit avec notre application
  • Le JavaScript va alors travailler et récupérer des données (en interrogeant un ou plusieurs serveurs si besoin)
  • Le JavaScript met à jour la page HTML pour répondre à l'interaction de l'utilisateur
<header>
    <div class="user-notlogged">Not logged in</div>
    <div class="user">
        <div class="user__firstname"></div>
        <div class="user__lastname"></div>
    </div>
</header>
<script>
$('form').submit(function(){
  var credentials = ...;
  $.post('/login', credentials, function( user ) {
      $('.user-notlogged').hide();
      $('.user').show();
      $('.user__firstname').text(user.firstname);
      $('.user__lastname').text(user.lastname);
  });
})
</script>

Le problème est qu'on est obligé de manipuler le code HTML, élément par élément, ce qui est loin d'être pratique. Au fur et à mesure que notre application va grandir, notre code va devenir de plus en plus complexe. jQuery à beau être pratique pour manipuler le DOM il s'avère inadapté comme base pour créer une application complète.

AngularJS, "HTML enhanced"

C'est à ce moment-là que sont arrivés des Frameworks plus poussés tels qu'AngularJS, BackboneJS ou EmberJS. Ces outils ne sont plus des librairies que l'on va juste utiliser pour régler quelques problèmes de compatibilité, mais de réel framework qui permettent de régler de multiples problèmes afin de créer une application entièrement basée sur le JavaScript. AngularJS s'est fait notamment remarquer par son approche qui est d'utiliser directement l'HTML pour la partie vue en le rendant dynamique à travers des directives. Je ne vais pas forcément rentrer dans les détails, mais pour produire le même résultat que précédemment :

<header ng-controller="UserCtrl">
    <div class="user-notlogged" ng-hide="user">Not logged in</div>
    <div class="user" ng-show="user">
        <div class="user__firstname">{{ user.firstname }}</div>
        <div class="user__lastname">{{ user.lastname }}</div>
    </div>
</header>

Ensuite AngularJS utilise un système de $scope qui va permettre d'injecter les variables dans l'HTML.

app.controller('UserCtrl', function ($scope, $http) {

  $scope.user = false

  $http.post('/login', credentials).success(function(user) {
    $scope.user = user;
  });

});

Ce n'est évidemment qu'un exemple restreint, car AngularJS propose aussi d'autres fonctionnalités comme un Router (pour gérer le changement d'URL), un injecteur de dépendance, etc...

Sur le papier c'est simple et idéal, mais en réalité cette approche n'est pas optimale, car le framework fait beaucoup de choses de manière automatique et du coup on ne sait pas trop ce qui se passe derrière. Pour fonctionner AngularJS, va détecter les modifications faites sur le $scope (l'objet magique qui permet de transférer les variables à la vue), va lancer une série de $watch qui permet de savoir si une variable a été modifiée. Si une variable a été modifiée, alors il mettra à jour le code HTML en fonction.

Le problème de cette approche est, qu'en grandissant, notre application va multiplier le nombre de comparaisons que devra faire le framework ce qui va avoir un impact négatif sur les performances. De la même façon on se retrouve aussi avec des éléments HTML qui se retrouve modifiés de manière plus ou moins anarchique suivant comment est construit notre code HTML. Ces problèmes de performances sont visibles sur des périphériques avec de faibles ressources (comme les mobiles) ou lorsque les données sont trop volumineuses. On peut toutefois limités la casse avec une bonne compréhension du Framework mais on finit par se battre contre le système qui était censé nous simplifier la vie.

React

D'autres librairies se sont créées pour répondre à la problématique : Rendre l'HTML "réactif". L'exemple le plus populaire en ce moment est React (crée par Facebook), mais il en existe d'autres comme RiotJS, VueJS). Ces librairies sont un petit peu particulières parce qu'elles se focalisent seulement sur la partie vue HTML. Le principe de ces librairies est simple, on va décrire un état (un objet JavaScript simple ou un tableau) et la librairie va se charger de modifier le DOM en fonction de cet état. Ensuite, quand l'état est modifié, le DOM sera mis à jour pour refléter les modifications. On peut alors se demander, en quoi est-ce différent du système de $scope d'AngularJS ?

Dans le cas de React, la librairie met un intermédiaire entre le JavaScript et le Dom : Le Virtual DOM. Lorsque notre état est modifié il ne va pas directement se mettre à tripatouiller notre DOM, mais va plutôt générer un DOM virtuel, il va alors comparer ce DOM virtuel au DOM virtuel de l'état précédent et déterminer les différences. À partir de ces différences, il va être capable de déterminer comment modifier le vrai DOM de la manière la plus optimale possible.

Alors dit comme ça on peut se dire

Wow c'est complètement con ! comment une idée pareille peut être plus rapide que de manipuler le DOM directement ?

Les changements fait au DOM virtuel ne sont pas appliqués directement. Ainsi, React peut attendre la fin de sa boucle d'évènement avant de commencer à toucher le DOM réel. Il applique donc une combinaison des changements plutôt qu'une série de changements au DOM réel. Les interactions avec le DOM étant couteuses, en limitant ces interactions il optimise les performances.

/**
* React utilise une syntaxe appelée le JSX qui permet de rendre le code moins verbeux, surtout pour le virtualDOM
**/
var Timer = React.createClass({
  // Le système d'état dont on parlait précédemment
  getInitialState: function() {
    return {secondsElapsed: 0};
  },
  tick: function() {
    // Hop on change l'état, state, ce qui met à jour la vue
    this.setState({secondsElapsed: this.state.secondsElapsed + 1});
  },
  componentDidMount: function() {
    this.interval = setInterval(this.tick, 1000);
  },
  componentWillUnmount: function() {
    clearInterval(this.interval);
  },
  // Le VirtualDOM
  render: function() {
    return (
      <div>Ce contour tourne depuis : {this.state.secondsElapsed} secondes</div>
    );
  }
});

ReactDOM.render(<Timer />, document.getElementById('example'));

Le problème avec React et les autres librairies de ce type-là est qu'elles permettent de solutionner une partie du problème, mais contrairement à AngularJS qui propose un framework complet, on se retrouve ici avec un système qui ne permet que de gérer les éléments HTML (plus de controller, plus de router...).

Flux, "Application architecture for building user interfaces"

Ces nouvelles librairies nous laissent un peu dans le brouillard (comment faire communiquer plusieurs composants ensemble ?)

Facebook a rencontré ce problème assez rapidement avec de nombreux composants qui doivent communiquer entre eux (le chat, le chat étendu, le système de notification...) et propose une nouvelle organisation : Flux.

Je ne rentrerais pas ici dans le détail du fonctionnement de flux, car ça mérite un article à part entière, mais l'apparition de cette organisation a entrainé la création de nouvelles librairies qui permettent d'implémenter la structure Flux plus simplement telle que Redux.

L'organisation Flux

Pour résumé on a 3 composants :

  • Un dispatcher reçoit une action et la transmet aux Store
  • Un store qui reçoit l'action et modifie le state en fonction de l'action demandée et dit "j'ai changé !"
  • Les vues détectent quand un store dit "j'ai changé !" et se mettent à jour en fonction.

Cette organisation peut sembler complexe au premier abord (car elle l'est :D), mais permet de garder un code organisé et scalable. Lorsque l'application grandit, on ne se retrouve pas embrouillé comme ça peut être le cas avec la structure MVC.

And the winner is ?

Alors là vous vous dites

Sympa ton article mais je ne suis pas avancé ! Donc je dois utiliser React, facebook l'utilise !

Pas vraiment ! Ce que je cherche à mettre en évidence avec cet article c'est que le JavaScript et les librairies/frameworks associées ont évolué au fil des usages et de nouveaux outils se sont créés pour répondre à de nouvelles problématiques. Ce cheminement est aussi celui que l'on suit lorsque l'on apprend à développer. On commence par utiliser les outils de base, lorsque notre code commence à grandir et que l'on rencontre des problèmes (on n'arrive pas à s'organiser, ou l'on se répète trop souvent) alors on recherche des outils plus avancés pour répondre à nos nouvelles problématiques.

Il est important de suivre ce cheminement, surtout lorsque l'on débute. Facebook utilise React et Flux, car ils ont une application incroyablement complexe à créer. Si vous souhaitez mettre en place un simple carrousel, vous n'aurez alors pas besoin des mêmes outils. N'essayez pas de sauter les étapes et de vous jeter sur la dernière technologie à la mode, essayer de comprendre l'utilité de cette dernière avant de savoir si oui ou non vous en avez l'utilité.

Publié
Technologies utilisées
Auteur :
Grafikart
Partager