Introduction au cadre de réaction

Dans le monde actuel des frameworks d'application Javascript, la philosophie de conception est le facteur clé de différenciation. Si vous comparez les frameworks JS populaires, tels que EmberJS, AngularJS, Backbone, Knockout, etc., vous êtes sûr de trouver des différences dans leurs abstractions, leurs modèles de pensée et, bien sûr, leur terminologie. Ceci est une conséquence directe de la philosophie de conception sous-jacente. Mais, en principe, ils font tous une chose, qui consiste à faire abstraction du DOM de manière à ne pas traiter directement avec les éléments HTML..

Je pense personnellement qu'un cadre devient intéressant lorsqu'il fournit un ensemble d'abstractions permettant un mode de pensée différent. Dans cet aspect, réagissez, le nouveau cadre JS des responsables de Facebook vous obligera à repenser (dans une certaine mesure) la façon dont vous décomposez l'interface utilisateur et les interactions de votre application. Ayant atteint la version 0.4.1 (à ce jour), React fournit un modèle à la fois simple et efficace pour créer des applications JS qui mélange un délicieux cocktail d’un genre différent..

Dans cet article, nous allons explorer les éléments constitutifs de React et adopter un style de pensée qui peut sembler contre-intuitif dès le départ. Mais, comme le dit la documentation de React: "Donnez-lui cinq minutes" et vous verrez ensuite comment cette approche deviendra plus naturelle..

Les motivations

L'histoire de React a commencé dans les confins de Facebook, où elle s'est brassée pendant un moment. Ayant atteint un état suffisamment stable, les développeurs ont décidé de l’ouvrir en source quelques mois auparavant. Fait intéressant, le site Web Instagram est également optimisé par le framework React.

React aborde le problème de l'abstraction DOM avec une approche légèrement différente. Pour comprendre en quoi cela est différent, passons rapidement en revue les techniques adoptées par les cadres que j'ai mentionnés plus tôt..

Vue d'ensemble des cadres d'application JS

Le modèle de conception MVC (Model-View-Controller) est fondamental pour le développement d'interface utilisateur, non seulement dans les applications Web, mais également dans les applications frontales sur toutes les plateformes. Dans le cas d'applications Web, le DOM est la représentation physique d'une vue. Le DOM lui-même est généré à partir d'un modèle HTML textuel extrait d'un fichier différent, d'un bloc de script ou d'une fonction de modèle précompilée. le Vue est une entité qui donne vie au modèle textuel en tant que fragment DOM. Il met également en place des gestionnaires d’événements et s’occupe de manipuler l’arbre DOM dans le cadre de son cycle de vie..

Pour le Vue pour être utile, il doit afficher certaines données et éventuellement permettre l’interaction de l’utilisateur. Les données sont les Modèle, qui provient d'une source de données (base de données, service Web, stockage local, etc.). Les cadres permettent de "lier" les données à la vue, de telle sorte que les modifications apportées aux données sont automatiquement répercutées avec les modifications apportées à la vue. Ce processus automatique s'appelle liaison de données et il y a des API / techniques pour rendre cela aussi transparent que possible.

La triade MVC est complétée par le Manette, qui engage le Vue et le Modèle et orchestre le flux de données (Modèle) dans le Vue et des événements utilisateur sur le Vue, conduisant éventuellement à des changements dans la Modèle.


Les cadres qui gèrent automatiquement le flux de données entre la vue et le modèle conservent une boucle d'événement interne. Cette boucle d'événement est nécessaire pour écouter certains événements utilisateur, événements de modification de données, déclencheurs externes, etc., puis pour déterminer s'il y a eu des modifications par rapport à l'exécution précédente de la boucle. S'il y a des changements, à l'une ou l'autre extrémité (View ou Model), l'infrastructure veille à ce que les deux soient ramenés en synchronisation.

Ce qui fait réagir différemment?

Avec React, la partie vue de la triade MVC prend de l’importance et est intégrée à une entité appelée Composant. Le composant maintient un sac de propriété immuable appelé les accessoires, et un Etat cela représente l'état utilisateur de l'interface utilisateur. La partie génération de vues du Composant est plutôt intéressant et peut-être la raison pour laquelle React se démarque des autres frameworks. Au lieu de construire un DOM physique directement à partir d’un fichier modèle / script / fonction, le Composant génère un DOM intermédiaire qui remplace le réel DOM HTML. Une étape supplémentaire est ensuite prise pour traduire ce DOM intermédiaire dans le réel DOM HTML..

Dans le cadre de la génération de DOM intermédiaire, le Composant attache également des gestionnaires d’événements et lie les données contenues dans les accessoires et Etat.

Si l'idée d'un DOM intermédiaire semble un peu étrange, ne vous inquiétez pas trop. Vous avez déjà vu cette stratégie adoptée par les runtimes de langage (ou machines virtuelles) pour les langages interprétés. Notre propre moteur d'exécution JavaScript génère d'abord une représentation intermédiaire avant de cracher le code natif. Ceci est également vrai pour d'autres langages basés sur des machines virtuelles tels que Java, C #, Ruby, Python, etc..

React adopte intelligemment cette stratégie pour créer un DOM intermédiaire avant de générer le DOM HTML final. Le DOM-intermédiaire est juste un graphe d'objet JavaScript et n'est pas rendu directement. Il y a une étape de traduction qui crée le vrai DOM. C’est la technique sous-jacente qui permet à React d’effectuer des manipulations rapides du DOM.

Réagir en profondeur

Pour avoir une meilleure idée de la façon dont React fait tout fonctionner, plongons un peu plus loin; en commençant par le Composant. Le composant est le bloc de construction principal de React. Vous pouvez composer l'interface utilisateur de votre application en assemblant une arborescence de composants. Chaque composant fournit une implémentation pour le rendre() méthode, où il crée le DOM intermédiaire. Appel React.renderComponent () Sur le composant racine, il en résulte une descente récursive de l'arborescence des composants et la construction du DOM intermédiaire. Le DOM intermédiaire est ensuite converti en réel DOM HTML..


Etant donné que la création de DOM intermédiaire fait partie intégrante du composant, React fournit une extension commode à JavaScript basée sur XML, appelée JSX, permettant de créer l’arborescence des composants sous la forme d’un ensemble de nœuds XML. Cela facilite la visualisation et le raisonnement sur le DOM. JSX simplifie également l'association des gestionnaires d'événements et des propriétés en tant qu'attributs xml. Comme JSX est un langage d'extension, il existe un outil (en ligne de commande et dans le navigateur) pour générer le code JavaScript final. Un nœud XML JSX mappe directement à un composant. Il convient de souligner que React fonctionne indépendamment de JSX et que le langage JSX ne permet que de créer facilement le DOM intermédiaire..

Outillage

Le cadre de base de React peut être téléchargé à partir de leur site Web. En outre, pour la transformation JSX → JS, vous pouvez utiliser le fichier JSXTransformer intégré au navigateur ou utiliser l'outil de ligne de commande, appelé react-tools (installé via NPM). Vous aurez besoin d’une installation de Node.js pour le télécharger. L'outil de ligne de commande vous permet de précompiler les fichiers JSX et d'éviter la traduction dans le navigateur. Ceci est certainement recommandé si vos fichiers JSX sont volumineux ou nombreux..

Un composant simple

Très bien, nous avons vu beaucoup de théorie jusqu’à présent, et je suis sûr que vous avez hâte de voir du vrai code. Passons à notre premier exemple:

/ ** @jsx React.DOM * / var Simple = React.createClass (getInitialState: function () return count: 0;, handleMouseDown: function () alert ('On m'a dit:' + ceci. props.message); this.setState (count: this.state.count + 1);, rendu: function () return 
Donne moi le message!
Message transmis this.state.count fois)
; ); React.renderComponent (, document.body);

Bien que simple, le code ci-dessus couvre une bonne partie de la surface de React:

  • Nous créons le composant Simple en utilisant React.createClass et en passant dans un objet qui implémente certaines fonctions essentielles. Le plus important est le rendre(), qui génère le DOM intermédiaire.
  • Ici, nous utilisons JSX pour définir le DOM et associer également le gestionnaire d’événements mousedown. le La syntaxe est utile pour incorporer des expressions JavaScript pour les attributs (onMouseDown = this.handleClick) et des nœuds enfants (this.state.count). Les gestionnaires d'événements associés à l'aide de la syntaxe sont automatiquement liés à l'instance du composant. Ainsi ce à l'intérieur de la fonction de gestionnaire d'événement fait référence à l'occurrence du composant. Le commentaire sur la première ligne / ** @jsx React.DOM * / est un signal pour que le transformateur JSX fasse la traduction en JS. Sans cette ligne de commentaire, aucune traduction n'aura lieu.

Nous pouvons exécuter l'outil de ligne de commande (jsx) en mode veille et compiler automatiquement les modifications de JSX → JS. Les fichiers source sont en / src dossier et la sortie est générée dans /construire.

jsx --watch src / build /

Voici le fichier JS généré:

/ ** @jsx React.DOM * / var Simple = React.createClass (displayName: 'Simple', getInitialState: function () return count: 0;, handleMouseDown: function () alert ('I was dit: '+ this.props.message); this.setState (count: this.state.count + 1);, rendre: function () return React.DOM.div (null, React.DOM.div. (className: "clicker", onMouseDown: this.handleMouseDown, "Give me the message!"), React.DOM.div (className: "message", "Message transmis", React.DOM.span ( className: "count", this.state.count), "time (s)"));); React.renderComponent (Simple (message: "Keep it Simple"), document.body);

Remarquez comment

et les étiquettes mappent vers des instances de React.DOM.div et React.DOM.span.

  • Revenons maintenant à notre exemple de code. À l'intérieur handleMouseDown, nous nous servons de this.props lire le message propriété qui a été passé en. Nous avons mis la message sur la dernière ligne de l'extrait, dans l'appel à React.renderComponent () où nous créons le composant. Le but de this.props est de stocker les données qui ont été transmises au composant. Il est considéré comme immuable et seul un composant de niveau supérieur est autorisé à effectuer des modifications et à le transmettre à l'arborescence des composants..
  • À l'intérieur handleMouseDown nous définissons également un état utilisateur avec this.setState () pour suivre le nombre d'affichages du message. Vous remarquerez que nous utilisons cet.etat dans le rendre() méthode. Chaque fois que vous appelez setState (), Réagir déclenche également la rendre() méthode pour garder le DOM en synchronisation. outre React.renderComponent (), setState () est un autre moyen de forcer un rafraîchissement visuel.

Événements synthétiques

Les événements exposés sur le DOM intermédiaire, tels que le onMouseDown, agissent également en tant que couche d'indirection avant qu'ils ne soient définis sur le real-DOM. Ces événements sont donc désignés sous le nom de Événements synthétiques. React adopte la délégation d'événements, qui est une technique bien connue, et attache les événements uniquement au niveau racine du real-DOM. Ainsi, il n'y a qu'un seul vrai gestionnaire d'événements sur le real-DOM. De plus, ces événements synthétiques offrent également un niveau de cohérence en masquant les différences entre les navigateurs et les éléments..

La combinaison des événements intermédiaires-DOM et synthétiques vous offre un moyen standard et cohérent de définir des interfaces utilisateur sur différents navigateurs et même périphériques..

Cycle de vie des composants

Les composants du framework React ont un cycle de vie spécifique et incorporent une machine à états à trois états distincts..


La composante prend vie après avoir été Monté. Le montage entraîne le passage d'un render-pass générant l'arborescence des composants (intermédiaire-DOM). Cet arbre est converti et placé dans un noeud de conteneur du vrai DOM. C’est le résultat direct de l’appel à React.renderComponent ().

Une fois monté, le composant reste dans le Mettre à jour Etat. Un composant est mis à jour lorsque vous changez d'état à l'aide de setState () ou changer les accessoires en utilisant setProps (). Cela se traduit par un appel rendre(), qui met le DOM en synchronisation avec les données (les accessoires + Etat). Entre les mises à jour ultérieures, React calculera le delta entre l’arborescence des composants précédente et l’arborescence nouvellement générée. C'est une étape hautement optimisée (et une fonctionnalité phare) qui minimise la manipulation sur le vrai DOM.

L'état final est Non monté. Cela se produit lorsque vous appelez explicitement React.unmountAndReleaseReactRootNode () ou automatiquement si un composant était un enfant qui n'était plus généré dans un rendre() appel. Le plus souvent, vous n'avez pas à vous en préoccuper et à laisser React faire le bon choix.

C’était bien grave, si React ne vous avait pas dit quand il se serait déplacé entre le Monté-mise à jour-non monté États. Heureusement, ce n'est pas le cas et vous pouvez remplacer certains crochets pour être averti des modifications apportées au cycle de vie. Les noms parlent d'eux-mêmes:

  • getInitialState (): prépare l'état initial du composant
  • composantWillMount ()
  • composantDidMount ()
  • composantWillReceiveProps ()
  • shouldComponentUpdate (): utile si vous voulez contrôler quand un rendu doit être ignoré.
  • composantWillUpdate ()
  • rendre()
  • composantDidUpdate ()
  • composantWillUnmount ()

le composantWill * les méthodes sont appelées avant le changement d'état et la composantDid * les méthodes sont appelées après.

Certains noms de méthodes semblent avoir été inspirés par les frameworks Cocoa sous Mac et iOS.

Caractéristiques diverses

Dans une arborescence de composants, les données doivent toujours être transmises vers le bas. Un composant parent doit définir la les accessoires d'un composant enfant pour transmettre les données du parent à l'enfant. Ceci est appelé comme Propriétaire paire. Par ailleurs, les événements utilisateur (souris, clavier, touches) bouillonneront toujours de l’enfant jusqu’au composant racine, à moins qu’ils ne soient gérés entre.


Lorsque vous créez le DOM intermédiaire dans rendre(), vous pouvez aussi assigner un ref propriété à un composant enfant. Vous pouvez ensuite vous y référer depuis le parent en utilisant le refs propriété. Ceci est décrit dans l'extrait ci-dessous.

 render: function () // Définit un retour de référence 
this.state.count
; handleMouseDown: function () // Utilise la ref console.log (this.refs.counter.innerHTML); ,

Dans le cadre des métadonnées du composant, vous pouvez définir l’état initial (getInitialState ()), que nous avons vu précédemment dans les méthodes du cycle de vie. Vous pouvez également définir les valeurs par défaut des accessoires avec getDefaultProps () et aussi établir des règles de validation sur ces accessoires en utilisant propTypes. La documentation donne un bon aperçu des différents types de validations (vérifications de type, obligatoires, etc.) que vous pouvez effectuer..

React soutient également le concept de Mixin extraire des éléments de comportement réutilisables pouvant être injectés dans des composants disparates. Vous pouvez passer les mixins en utilisant le mixins propriété d'un composant.

Maintenant, passons aux choses réelles et construisons un composant plus complet qui utilise ces fonctionnalités.

Un éditeur de forme construit en utilisant React

Dans cet exemple, nous allons construire un éditeur qui accepte un simple DSL (Domain Specific Language) pour créer des formes. Au fur et à mesure que vous tapez, vous verrez la sortie correspondante sur le côté, ce qui vous donnera des informations en direct.

Le DSL vous permet de créer trois types de formes: Ellipse, Rectangle et Texte. Chaque forme est spécifiée sur une ligne distincte avec un ensemble de propriétés de style. La syntaxe est simple et emprunte un peu à CSS. Pour analyser une ligne, nous utilisons un regex qui ressemble à ceci:

 var shapeRegex = / (rect | ellipse | text) (\ s [a-z] +: \ s [a-z0-9] +;) * / i;

À titre d’exemple, l’ensemble de lignes suivant décrit deux rectangles et une étiquette de texte…

// valeur du texte de l'étiquette de réaction: React; couleur: # 00D8FF; taille de police: 48px; ombre du texte: 1px 1px 3px # 555; rembourrage: 10px; à gauche: 100px; en haut: 100px; // gauche logo rect fond: url (react.png) no-repeat; bordure: aucune; largeur: 38; hauteur: 38; à gauche: 60px; en haut: 120 px; // logo de fond droit: url (react.png) no-repeat; bordure: aucune; largeur: 38; hauteur: 38; à gauche: 250 pixels; en haut: 120 px;

… Générer la sortie indiquée ci-dessous:


Mise en place

Bon, allons de l'avant et construisons cet éditeur. Nous commencerons par le fichier HTML (index.html), où nous mettons dans le balisage de niveau supérieur et incluons les bibliothèques et les scripts d’application. Je ne montre que les parties pertinentes ici:

  

Dans l'extrait ci-dessus, le récipient div détient notre DOM généré par React. Nos scripts d’application sont inclus à partir du /construire annuaire. Nous utilisons JSX dans nos composants et l’observateur de ligne de commande (jsx), met les fichiers JS convertis en /construire. Notez que cette commande watcher fait partie de la outils de réaction Module NPM.

jsx --watch src / build /

L'éditeur est divisé en un ensemble de composants, énumérés ci-dessous:

  • Editeur de forme: le composant racine dans l'arborescence des composants
  • ShapeCanvas: responsable de la génération des composants de forme (Ellipse, Rectangle, Texte). Il est contenu dans le ShapeEditor.
  • ShapeParser: responsable de l'analyse du texte et de l'extraction de la liste des définitions de formes. Il analyse ligne par ligne le Regex que nous avons vu précédemment. Les lignes non valides sont ignorées. Ce n'est pas vraiment un composant, mais un objet JS d'assistance, utilisé par ShapeEditor.
  • Ellipse, Rectangle, Texte: la forme des composants. Ceux-ci deviennent des enfants des ShapeCanvas.
  • ShapePropertyMixin: fournit des fonctions d'assistance pour extraire les styles trouvés dans les définitions de formes. Ceci est mélangé dans les trois composants de forme en utilisant le mixins propriété.
  • app: le point d’entrée de l’éditeur. Il génère le composant racine (ShapeEditor) et vous permet de choisir un échantillon de forme dans la liste déroulante..

La relation entre ces entités est montrée dans l'arborescence des composants annotée:


Le composant ShapeEditor

Regardons l’implémentation de certains de ces composants, en commençant par ShapeEditor.

/ ** @jsx React.DOM * / var ShapeEditor = React.createClass (composantWillMount: function () this._parser = new ShapeParser ();, getInitialState: function () return text: ",, render: function () var shape = this._parser.parse (this.state.text); var tree = (