Développer votre propre framework et la croissance professionnelle d'un programmeur JS

Vous êtes-vous déjà demandé comment fonctionnent les frameworks? L'auteur du document, dont nous publions la traduction aujourd'hui, dit que lorsqu'il, il y a de nombreuses années, après avoir étudié jQuery , est tombé sur Angular.js , ce qu'il a vu lui a semblé très compliqué et incompréhensible. Puis Vue.js est apparu, et traitant de ce cadre, il a été inspiré pour écrire son propre système de liaison de données bidirectionnelle. De telles expériences contribuent au développement professionnel du programmeur. Cet article est destiné à ceux qui souhaitent approfondir leurs propres connaissances dans le domaine des technologies sur lesquelles sont basés les frameworks JS modernes. En particulier, il se concentrera sur la façon d'écrire le cœur de votre propre infrastructure qui prend en charge les attributs personnalisés des éléments HTML, la réactivité et la liaison de données bidirectionnelle.

image

À propos du système de réactivité


Ce sera bien si, au tout début, nous découvrons comment fonctionnent les systèmes de réactivité des cadres modernes. En fait, tout ici est assez simple. Par exemple, Vue.js, lors de la déclaration d'un nouveau composant, procède au proxy de chaque propriété (getters et setters) en utilisant le modèle de conception «proxy ».

En conséquence, le framework sera capable de détecter chaque fait que la valeur change, exécuté à la fois à partir du code et de l'interface utilisateur.

Modèle de proxy


Le modèle de proxy est basé sur une surcharge des mécanismes d'accès à un objet. Cela ressemble à la façon dont les gens travaillent avec leurs comptes bancaires.

Par exemple, personne ne peut travailler directement avec son compte, en modifiant son solde en fonction de ses besoins. Pour effectuer des actions avec le compte, vous devez contacter une personne autorisée à travailler avec lui, c'est-à-dire à la banque. La banque agit en tant que mandataire entre le titulaire du compte et le compte lui-même.

var account = {    balance: 5000 } var bank = new Proxy(account, {    get: function (target, prop) {        return 9000000;    } }); console.log(account.balance); // 5,000 ( ) console.log(bank.balance);    // 9,000,000 (   ) console.log(bank.currency);   // 9,000,000 (  ) 

Dans cet exemple, lors de l'utilisation de l'objet bank pour accéder au solde du compte représenté par l'objet account , la fonction getter est surchargée, ce qui conduit au fait qu'à la suite d'une telle demande, la valeur 9 000 000 est toujours renvoyée, au lieu de la valeur immobilière, même si Cette propriété n'existe pas.

Et voici un exemple de surcharge d'une fonction de définition.

 var bank = new Proxy(account, {   set: function (target, prop, value) {       //      0       return Reflect.set(target, prop, 0);   } }); account.balance = 5800; console.log(account.balance); // 5,800 bank.balance = 5400; console.log(account.balance); // 0 (  ) 

Ici, en surchargeant la fonction set , vous pouvez contrôler son comportement. Par exemple, vous pouvez modifier la valeur que vous souhaitez écrire dans une propriété, écrire des données dans une autre propriété ou même ne rien faire.

Exemple de système de réactivité


Maintenant que nous avons compris le modèle de proxy, nous allons commencer à développer notre propre framework JS.

Afin de ne pas le compliquer, nous utiliserons une syntaxe très similaire à celle utilisée dans Angular.js. Par conséquent, la déclaration du contrôleur et la liaison des éléments de modèle aux propriétés du contrôleur seront simples et claires.

Voici le code du modèle.

 <div ng-controller="InputController">   <!-- "Hello World!" -->   <input ng-bind="message"/>     <input ng-bind="message"/> </div> <script type="javascript"> function InputController () {     this.message = 'Hello World!'; } angular.controller('InputController', InputController); </script> 

Vous devez d'abord déclarer un contrôleur avec des propriétés. Ensuite, utilisez ce contrôleur dans le modèle, et enfin, utilisez l'attribut ng-bind afin d'établir une liaison de données bidirectionnelle pour la valeur de l'élément.

Analyser un modèle et instancier un contrôleur


Pour que nous puissions avoir certaines propriétés auxquelles les données peuvent être attachées, nous avons besoin d'un endroit (contrôleur) où ces propriétés peuvent être déclarées. Ainsi, il est nécessaire de décrire le contrôleur et de l'inclure dans le cadre.

Dans le processus de travail avec les contrôleurs, le framework recherchera les éléments qui ont ng-controller attributs ng-controller . Si ce que vous pouvez trouver correspond à l'un des contrôleurs déclarés, le framework créera une nouvelle instance de ce contrôleur. Cette instance de contrôleur n'est responsable que de ce fragment particulier du modèle.

 var controllers = {}; var addController = function (name, constructor) {   //     controllers[name] = {       factory: constructor,       instances: []   };     // ,     var element = document.querySelector('[ng-controller=' + name + ']');   if (!element){      return; //  ,      }     //         var ctrl = new controllers[name].factory;   controllers[name].instances.push(ctrl);     //   ..... }; addController('InputController', InputController); 

Ci-dessous, la déclaration des controllers variables "maison". Notez que l'objet controllers contient tous les contrôleurs déclarés dans le framework en appelant addController .

 var controllers = {   InputController: {       factory: function InputController(){           this.message = "Hello World!";       },       instances: [           {message: "Hello World"}       ]   } }; 

Chaque contrôleur a une fonction d' factory , ceci est fait de sorte que, si nécessaire, vous pouvez créer une instance d'un nouveau contrôleur. De plus, le framework stocke, dans la propriété instances , toutes les instances du contrôleur du même type utilisé dans le modèle.

Rechercher des éléments impliqués dans la liaison de données


Pour le moment, nous avons une instance du contrôleur et un fragment du modèle utilisant ce contrôleur. Notre prochaine étape sera de rechercher des éléments avec des données que vous devez lier aux propriétés du contrôleur.

 var bindings = {}; //  : element   DOM,   Array.prototype.slice.call(element.querySelectorAll('[ng-bind]'))   .map(function (element) {       var boundValue = element.getAttribute('ng-bind');       if(!bindings[boundValue]) {           bindings[boundValue] = {               boundValue: boundValue,               elements: []           }       }       bindings[boundValue].elements.push(element);   }); 

L'organisation du stockage de toutes les liaisons d'objets à l'aide d'une table de hachage est illustrée ici. La variable considérée contient toutes les propriétés de liaison, avec leurs valeurs actuelles, et tous les éléments DOM liés à une propriété spécifique.

Voici à quoi ressemble notre version de la variable bindings :

     var bindings = {       message: {           //   :           // controllers.InputController.instances[0].message           boundValue: 'Hello World',           // HTML- (   ng-bind="message")           elements: [               Object { ... },               Object { ... }           ]       }   }; 

Liaison bilatérale des propriétés du contrôleur


Une fois que le framework a terminé la préparation préliminaire, le moment est venu pour une chose intéressante: la liaison de données bidirectionnelle. La signification de ceci est la suivante. Premièrement, les propriétés du contrôleur doivent être liées aux éléments DOM, ce qui leur permettra d'être mis à jour lorsque les valeurs des propriétés changent par rapport au code, et deuxièmement, les éléments DOM doivent également être liés aux propriétés du contrôleur. De ce fait, lorsque l'utilisateur agit sur de tels éléments, cela entraîne une modification des propriétés du contrôleur. Et si plusieurs éléments HTML sont attachés à la propriété, cela conduit également au fait que leur état est également mis à jour.

Détection des modifications apportées à partir du code à l'aide d'un proxy


Comme mentionné ci-dessus, Vue.js encapsule les composants dans un proxy afin de détecter les changements de propriétés. Nous ferons de même en mandatant uniquement le setter pour les propriétés du contrôleur impliquées dans la liaison de données:

 //  : ctrl -    var proxy = new Proxy(ctrl, {   set: function (target, prop, value) {       var bind = bindings[prop];       if(bind) {           //    DOM,               bind.elements.forEach(function (element) {               element.value = value;               element.setAttribute('value', value);           });       }       return Reflect.set(target, prop, value);   } }); 

Par conséquent, il s'avère que lorsque la valeur est écrite dans la propriété liée, le proxy détectera tous les éléments liés à cette propriété et leur transmettra une nouvelle valeur.

Dans cet exemple, nous prenons uniquement en charge la liaison des éléments d' input , car seul l'attribut value est défini ici.

Réponse aux événements d'élément


Il ne nous reste plus qu'à fournir une réponse du système aux actions des utilisateurs. Pour ce faire, tenez compte du fait que les éléments DOM déclenchent des événements lorsqu'ils détectent des changements dans leurs valeurs.

Nous organisons l'écoute de ces événements et l'enregistrement dans les propriétés associées aux éléments des nouvelles données issues des événements. Tous les autres éléments liés à la même propriété seront, grâce au proxy, mis à jour automatiquement.

 Object.keys(bindings).forEach(function (boundValue) {   var bind = bindings[boundValue];     //          bind.elements.forEach(function (element) {     element.addEventListener('input', function (event) {       proxy[bind.boundValue] = event.target.value; //  ,          });   }) }); 

Résumé


En conséquence, en rassemblant tout ce dont nous avons discuté ici, vous obtiendrez votre propre système qui implémente les mécanismes de base des frameworks JS modernes. Voici un exemple pratique de la mise en œuvre d'un tel système. Nous espérons que ce matériel aidera tout le monde à mieux comprendre le fonctionnement des outils de développement Web modernes.

Chers lecteurs! Si vous utilisez professionnellement des frameworks JS modernes, veuillez nous expliquer comment vous avez commencé leur étude et ce qui vous a aidé à les comprendre.

Source: https://habr.com/ru/post/fr416021/


All Articles