Developpez.com - Rubrique Dév. Web

Le Club des Développeurs et IT Pro

Apprendre le data-binding dans Angular, React et Vue,

Un tutoriel de SylvainPV

Le 2016-04-03 01:55:13, par Community Management, Community Manager
Chers membres du club,

Je vous présente ce tutoriel de SylvainPV sur comprendre le data binding dans Angular, React et Vue.


Les frameworks MVVM sont aujourd'hui une partie centrale du développement front-end d'applications web. Ils occupent souvent l'actualité JavaScript et sont source de grands débats parmi les professionnels.

Derrière cette appellation, on retrouve un principe de base reliant le Modèle (M) à la Vue (V) : le data binding. Pourtant ce mécanisme est souvent mal connu ou mal compris par les utilisateurs de ces frameworks, perdus dans le jargon technique et marketing.

Nous allons tâcher de décrire comment ce mécanisme de data binding est implémenté au sein de trois frameworks populaires : Angular, React et Vue.
Bonne lecture

Les meilleurs cours et tutoriels pour apprendre la programmation JavaScript
Les meilleurs cours et tutoriels pour apprendre la programmation Web
  Discussion forum
11 commentaires
  • Paleo
    Membre éclairé
    L'approche de Monkberry semble saine : pas de data binding, pas de DOM virtuel, aucune magie.

    Un langage de template :

    Code :
    1
    2
    3
    <div>
      Hello {{ name }}!
    </div>
    … compilé en code JavaScript utilisant les objets DOM standards :

    Code :
    1
    2
    var div1 = document.createElement('div');
    div1.textContent = 'Hello ' + name + '!';
  • SylvainPV
    Rédacteur/Modérateur
    Intéressant. C'est bien du data-binding, à partir du moment où il y a des mises à jour partielles du DOM et une résolution des liaisons dans un modèle de données. Pour la magie c'est plus subjectif comme définition J'ai joué avec une petite heure, voilà mon analyse :

    Pour la détection de changements, c'est du classique: une API de changement d'état à la React/Backbone. Le templating est DOM-based (on ne peut pas mettre d'expressions pour définir un tag par exemple), mais il y a les moustaches comme sucre syntaxique pour l'interpolation de texte et attributs.

    Concernant la mise à jour du DOM, c'est plus particulier : la compilation du template consiste en fait à le faire passer par un parser HTML qui va écrire le code JS correspondant pour générer ce HTML à partir des API du DOM, avec le bon vieux document.createElement. Ces éléments sont créés un par un et assignés à des références en mémoire. Lorsque des liaisons de données sont détectées pour un élément, chaque donnée aura un setter associé qui fera les modifications sur l'élément en question. C'est un peu comme si vous écriviez vos mises à jour du DOM à la main, sauf que là c'est un compilateur qui s'en charge.

    Un exemple vaut mieux qu'un long discours. Le template:
    Code html :
    1
    2
    3
    4
    5
    6
    7
      
    <div> 
       <p> 
          Hello {{ name }}! 
       </p> 
       <p>Monkberry is <i>almost</i> an anagram of Monkey beer</p> 
    </div>

    est compilé en :

    Code js :
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    /** 
     * @class 
     */ 
    function hello() { 
      Monkberry.call(this); 
      
      // Create elements 
      var div0 = document.createElement('div'); 
      var p1 = document.createElement('p'); 
      var text2 = document.createTextNode(''); 
      var p3 = document.createElement('p'); 
      var i4 = document.createElement('i'); 
      
      // Construct dom 
      p1.appendChild(document.createTextNode(" Hello ")); 
      p1.appendChild(text2); 
      p1.appendChild(document.createTextNode("! ")); 
      i4.appendChild(document.createTextNode("almost")); 
      p3.appendChild(document.createTextNode("Monkberry is ")); 
      p3.appendChild(i4); 
      p3.appendChild(document.createTextNode(" an anagram of Monkey beer")); 
      div0.appendChild(p1); 
      div0.appendChild(p3); 
      
      // Update functions 
      this.__update__ = { 
        name: function (name) { 
          text2.textContent = name; 
        } 
      }; 
      
      // Set root nodes 
      this.nodes = [div0]; 
    } 
    hello.prototype = Object.create(Monkberry.prototype); 
    hello.prototype.constructor = hello; 
    hello.pool = []; 
    hello.prototype.update = function (__data__) { 
      if (__data__.name !== undefined) { 
        this.__update__.name(__data__.name); 
      } 
    }; 
      
    window.hello = hello; 
    //# sourceMappingURL=view.js.map

    Les avantages et les inconvénients apparaissent alors clairement:

    Avantages:
    • les mises à jour du DOM sont effectivement les plus minimales possibles, puisque chaque donnée a son setter dédié
    • le template une fois compilé reste lisible et exploitable en tant que tel


    Inconvénients:
    • une fois compilé, le code est très, très verbeux. Avec cet exemple très simple on a multiplié par 10 la taille du code après compilation. Et plus les liaisons dans le template seront complexes, plus ce facteur risque d'augmenter.
    • il n'y a actuellement pas d'optimisation pour gérer les parties statiques des templates. Le second paragraphe est ainsi construit manuellement avec ses références sans que cela soit vraiment nécessaire.


    Vu que la latence réseau reste le problème de perf numéro 1 pour l'usage web, le fait que la précompilation vienne décupler la taille des templates est un énorme désavantage, que le faible poids de la lib en elle-même peut difficilement compenser à lui seul (et puis afficher le poids gzippé c'est tricher :p ). A la rigueur, je peux y voir un intérêt pour les applications JS hors-ligne ou embarquées. Mais alors le faible poids de la lib n'est plus un argument en sa faveur non plus.

    J'ai aussi de grosses craintes sur la résolution de divers problèmes tels que les boucles infinies ou la résistance aux modifications inopinées du DOM. C'est le genre de chose pour lesquels Angular/React/Vue ont des mécanismes dédiés, j'ai moi-même dû le faire sur mon side-project de lib de data-binding. Mais en l'état, j'ai l'impression que Monkberry ne fait rien de tout ça et se contente d'une sorte de transpilation template HTML --> JS bête et méchante.

    Bref j'attends d'avoir plus de retours et des démos réelles d'applications pour juger, mais je suis très mitigé pour le moment.
  • Paleo
    Membre éclairé
    Merci SylvainPV pour cet excellent aperçu des différentes solutions en data-binding.

    Je trouve aussi que Vue.js est une solution légère et élégante en matière de data-binding. La question qui, en ce qui me concerne, n'est pas tranchée, est la suivante : le data-binding vaut-il réellement la peine, par rapport à une manipulation plus traditionnelle des éléments du DOM via Sizzle/jQuery ou même les API standards ?
  • SylvainPV
    Rédacteur/Modérateur
    Tout dépend si tu optes pour un rendu des vues côté client ou côté serveur. Si l'essentiel de ton rendu est fait côté serveur, et que les interactions côté client ne sont pas trop complexes, une manipulation directe et manuelle du DOM est sans doute plus simple. En revanche, si le rendu est fait côté client, on a tout intérêt à se reposer sur une solution de data-binding afin que toutes les vues soient conditionnés par le modèle. Cela permet de limiter les états applicatifs et de simplifier énormément la gestion. Tu es déjà sûrement tombé dans le cas où tu as un plugin UI jQuery qui doit modifier le DOM pour s' "initialiser", et sur lequel tu dois toujours te poser la question de s'il est initialisé ou pas, ou "en cours d'initialisation". Ces "états" de vues n'existent plus quand tu pars sur une solution comme React ou vuex.

    Si tu te rappelles de mon article sur le templating client, tu sais que je penche nettement en faveur du rendu côté client, pour la compensation de latence, la réduction de la bande-passante utilisée et l'ouverture à l'usage offline
  • Paleo
    Membre éclairé
    Je parle aussi d'application JavaScript embarquée dans le navigateur. Le data-binding n'est pas un lien entre le modèle et la vue au sens MVC du terme. Il s'agit plutôt d'un moyen d'agir sur la vue au travers d'une représentation. Je veux dire que les modèles des vues ne sont pas le modèle de l'application, et la synchronisation entre les (modèles de) vues et le modèle de l'application est en dehors de la problématique du data-binding.

    On peut programmer de manière modulaire, par "composants", c-à-d en regroupant le code CSS, les templates HTML et le code JS par petites entités fonctionnelles, tout en manipulant directement des éléments du DOM.
  • SylvainPV
    Rédacteur/Modérateur
    Oui, on se rapproche alors davantage d'un pattern MVC que MVVM.
  • Paleo
    Membre éclairé
  • Paleo
    Membre éclairé
    Merci pour l'analyse.

    Envoyé par SylvainPV
    C'est bien du data-binding, à partir du moment où il y a des mises à jour partielles du DOM et une résolution des liaisons dans un modèle de données.
    Le data binding n'implique-t-il pas par définition une synchronisation automatisée ? Ici, après chaque modification des données, il faut faire un :

    Code :
    view.update(state);
    Envoyé par SylvainPV
    C'est un peu comme si vous écriviez vos mises à jour du DOM à la main, sauf que là c'est un compilateur qui s'en charge.
    C'est-à-dire que Monkberry donne un langage de template à ceux qui veulent gérer des morceaux du DOM à l'ancienne.

    Envoyé par SylvainPV
    Vu que la latence réseau reste le problème de perf numéro 1 pour l'usage web, le fait que la précompilation vienne décupler la taille des templates est un énorme désavantage
    Ah, effectivement. D'expérience, en ce qui me concerne, le poids des templates dans une application JS n'est pas énorme relativement au code JS, mais c'est un point à vérifier.

    Envoyé par SylvainPV
    J'ai aussi de grosses craintes sur la résolution de divers problèmes tels que les boucles infinies ou la résistance aux modifications inopinées du DOM. C'est le genre de chose pour lesquels Angular/React/Vue ont des mécanismes dédiés, j'ai moi-même dû le faire sur mon side-project de lib de data-binding. Mais en l'état, j'ai l'impression que Monkberry ne fait rien de tout ça et se contente d'une sorte de transpilation template HTML --> JS bête et méchante.
    Boucles infinies : il faudrait que je vois un exemple, je ne comprends pas où est le risque.
    Pour les modifications inopinées du DOM : il me semble aussi que rien n'est géré.
  • SylvainPV
    Rédacteur/Modérateur
    Il faut voir ce que l'on entend par "synchronisation automatisée". Je décompose ça en trois points: détection de changements, résolution des liaisons et mise à jour du DOM. Le data-binding désigne surtout la partie intermédiaire qui relie modèle et DOM, à partir de là on a des techniques de change detection et de mises à jour du DOM très différentes selon les solutions comme vu dans l'article. Les React / Redux / Vuex et autres Flux-based fonctionnent tous avec une API de changement d'état par exemple.

    Pour les boucles infinies j'en parle en début de l'article.
  • Paleo
    Membre éclairé
    Envoyé par SylvainPV
    Pour les boucles infinies j'en parle en début de l'article.
    Le 2-way data binding pose plusieurs problématiques, dont la principale est celle de provoquer des boucles infinies dans certains cas. Un match de ping-pong peut survenir lorsqu'une mise à jour du modèle entraîne une mise à jour de la vue qui elle-même entraîne une mise à jour du modèle, qui elle-même entraîne une mise à jour de la vue, etc.
    Sauf erreur, cette problématique est en dehors du scope de Monkberry, puisqu'il n'y a pas de détection des changements. Et pas non plus de mise à jour automatisée d'un état à partir de valeurs d'un formulaires. Ici, c'est au développeur de faire attention à ce qu'il fait.