Maison  >  Article  >  interface Web  >  Seulement 30 lignes de code pour implémenter les techniques MVC_javascript en Javascript

Seulement 30 lignes de code pour implémenter les techniques MVC_javascript en Javascript

WBOY
WBOYoriginal
2016-05-16 15:15:271057parcourir

Depuis 2009 environ, MVC s'est progressivement illustré dans le domaine du front-end, pour finalement inaugurer une grosse explosion avec le lancement de React Native en 2015 : AngularJS, EmberJS, Backbone, ReactJS, RiotJS, VueJS... .. Une série de noms sont apparus et ont changé de manière spectaculaire. Certains d'entre eux ont progressivement disparu de la vue de tous, certains continuent de croître rapidement et certains ont déjà pris leur propre rôle dans un environnement écologique spécifique. Quoi qu’il en soit, MVC a et continuera d’influencer profondément la façon de penser et les méthodes de travail des ingénieurs front-end.

De nombreux exemples d'explication de MVC partent d'un certain concept d'un framework spécifique, comme la collection Backbone ou le modèle dans AngularJS. C'est certainement une bonne approche. Mais la raison pour laquelle un framework est un framework, et non une bibliothèque de classes (jQuery) ou un ensemble d'outils (Underscore), est qu'il existe de nombreux excellents concepts de conception et bonnes pratiques derrière eux. Ces essences de conception se complètent, sont imbriquées et. sont indispensables, il n'est pas facile de voir l'essence d'un certain modèle de conception à travers un cadre complexe en peu de temps.

C'est l'origine de cet essai : le code prototype créé pour aider tout le monde à comprendre le concept doit être aussi simple que possible, juste assez simple pour que tout le monde puisse comprendre le concept.

1. La base de MVC est le modèle d'observateur, qui est la clé pour réaliser la synchronisation entre le modèle et la vue
Pour plus de simplicité, chaque instance de modèle ne contient qu'une seule valeur primitive.

function Model(value) {
  this._value = typeof value === 'undefined' ? '' : value;
  this._listeners = [];
}
Model.prototype.set = function (value) {
  var self = this;
  self._value = value;
  // model中的值改变时,应通知注册过的回调函数
  // 按照Javascript事件处理的一般机制,我们异步地调用回调函数
  // 如果觉得setTimeout影响性能,也可以采用requestAnimationFrame
  setTimeout(function () {
    self._listeners.forEach(function (listener) {
      listener.call(self, value);
    });
  });
};
Model.prototype.watch = function (listener) {
  // 注册监听的回调函数
  this._listeners.push(listener);
};
// html代码:
<div id="div1"></div>
// 逻辑代码:
(function () {
  var model = new Model();
  var div1 = document.getElementById('div1');
  model.watch(function (value) {
    div1.innerHTML = value;
  });
  model.set('hello, this is a div');
})();

Avec l'aide du modèle d'observateur, nous avons réalisé que lorsque la méthode set du modèle est appelée pour changer sa valeur, le modèle est également mis à jour de manière synchrone, mais cette implémentation est très délicate car nous devons surveiller manuellement le changement de la valeur du modèle (via la méthode watch ) et transmettre une fonction de rappel. Existe-t-il un moyen de faciliter la liaison de la vue (un ou plusieurs nœuds dom) au modèle ?

2. Implémentez la méthode bind et liez le modèle et la vue

Model.prototype.bind = function (node) {
  // 将watch的逻辑和通用的回调函数放到这里
  this.watch(function (value) {
    node.innerHTML = value;
  });
};
// html代码:
<div id="div1"></div>
<div id="div2"></div>
// 逻辑代码:
(function () {
  var model = new Model();
  model.bind(document.getElementById('div1'));
  model.bind(document.getElementById('div2'));
  model.set('this is a div');
})();

Grâce à une simple encapsulation, la liaison entre la vue et le modèle a pris forme. Même si plusieurs vues doivent être liées, elle est facile à mettre en œuvre. Notez que bind est une méthode native sur le prototype de la classe Function, mais elle n'est pas étroitement liée à MVC. L'auteur aime vraiment le mot bind. Il est précis et concis, je couvre donc simplement la méthode native ici. . Plus près de nous, même si la complexité de la liaison a été réduite, cette étape nécessite toujours que nous la complétions manuellement. Est-il possible de dissocier complètement la logique de liaison du code métier ?

3. Implémentez un contrôleur pour découpler la liaison du code logique

Des amis attentifs ont peut-être remarqué que même si nous parlons de MVC, seule la classe Model apparaît dans l'article ci-dessus. Il est compréhensible que la classe View n'apparaisse pas. Après tout, HTML est une View prête à l'emploi (en fait). , cet article le mentionne également du début à la fin. En utilisant simplement HTML comme View, la classe View n'apparaît pas dans le code javascript), alors pourquoi la classe Controller est-elle invisible ? Ne vous inquiétez pas, en fait, ce qu'on appelle le « code logique » est un segment de code avec un degré élevé de couplage entre la logique du framework (appelons le prototype de cet article un framework) et la logique métier.
Si vous souhaitez laisser la logique de liaison au framework, vous devez indiquer au framework comment effectuer la liaison. Comme il est difficile de compléter une annotation en JS, nous pouvons effectuer cette couche de balisage dans la vue - utiliser l'attribut tag de HTML est un moyen simple et efficace.

function Controller(callback) {
  var models = {};
  // 找到所有有bind属性的元素
  var views = document.querySelectorAll('[bind]');
  // 将views处理为普通数组
  views = Array.prototype.slice.call(views, 0);
  views.forEach(function (view) {
    var modelName = view.getAttribute('bind');
    // 取出或新建该元素所绑定的model
    models[modelName] = models[modelName] || new Model();
    // 完成该元素和指定model的绑定
    models[modelName].bind(view);
  });
  // 调用controller的具体逻辑,将models传入,方便业务处理
  callback.call(this, models);
}




// html:
<div id="div1" bind="model1"></div>
<div id="div2" bind="model1"></div>
// 逻辑代码:
new Controller(function (models) {
  var model1 = models.model1;
  model1.set('this is a div');
});


Est-ce si simple ? C'est aussi simple que cela. L'essence de MVC est de compléter la logique métier dans le contrôleur et de modifier le modèle. En même temps, les modifications apportées au modèle entraînent des mises à jour automatiques de la vue. Ces logiques sont reflétées dans le code ci-dessus et prennent en charge plusieurs vues et plusieurs modèles. Bien que cela ne soit pas suffisant pour les projets de production, j'espère que cela sera quelque peu utile à l'apprentissage MVC de chacun.

Le code "framework" organisé avec les commentaires supprimés :

function Model(value) {
  this._value = typeof value === 'undefined' &#63; '' : value;
  this._listeners = [];
}
Model.prototype.set = function (value) {
  var self = this;
  self._value = value;
  setTimeout(function () {
    self._listeners.forEach(function (listener) {
      listener.call(self, value);
    });
  });
};
Model.prototype.watch = function (listener) {
  this._listeners.push(listener);
};
Model.prototype.bind = function (node) {
  this.watch(function (value) {
    node.innerHTML = value;
  });
};
function Controller(callback) {
  var models = {};
  var views = Array.prototype.slice.call(document.querySelectorAll('[bind]'), 0);
  views.forEach(function (view) {
    var modelName = view.getAttribute('bind');
    models[modelName] = models[modelName] || new Model();
    models[modelName].bind(view);
  });
  callback.call(this, models);
}

Post-scriptum :

Dans le processus d'apprentissage de Flux et Redux, bien que l'auteur maîtrise l'utilisation des outils, je le connais seulement mais je ne sais pas pourquoi j'ai toujours souligné "Flux évite MVC en faveur d'un flux de données unidirectionnel". dans la documentation officielle de ReactJS. Je ne comprends pas très bien. J'ai toujours l'impression que le flux de données unidirectionnel et MVC n'entrent pas en conflit. Je ne comprends pas pourquoi les deux sont opposés dans le document ReactJS. est un sans lui (éviter, éviter). Finalement, j'ai décidé de revenir à la définition de MVC et de l'étudier à nouveau. Bien que je copie et colle négligemment dans mon travail quotidien, nous devons quand même être volontaires et mâcher les mots de temps en temps, n'est-ce pas ? Cette méthode m'a vraiment aidé à comprendre cette phrase. Ici, je peux partager mes réflexions avec vous : La raison pour laquelle je pense que le flux de données unidirectionnel dans MVC et le flux est similaire peut être due au fait qu'il n'y a pas de distinction claire entre MVC et le modèle d'observateur. . Causé par la relation - MVC est basé sur le modèle d'observateur, tout comme le flux, donc la source de cette similarité est le modèle d'observateur, et non MVC et le flux eux-mêmes. Cette compréhension est également confirmée dans le livre de modèles de conception original du quatuor : "Le premier exemple, et peut-être le plus connu, du modèle Observer apparaît dans Smalltalk Model/View/Controller (MVC), le cadre d'interface utilisateur dans l'environnement Smalltalk [KP88". ] La classe Model de MVC joue le rôle de Sujet, tandis que View est la classe de base pour les observateurs.

Si les lecteurs souhaitent continuer à développer un tel prototype de jouet, vous pouvez vous référer aux instructions suivantes :

  • 1. Implémenter la liaison bidirectionnelle des balises de classe d'entrée
  • 2. Obtenez un contrôle précis de la portée contrôlée par le contrôleur Ici, un contrôleur contrôle l'ensemble de l'arborescence DOM
  • .
  • 3. Implémentez la logique de masquage/affichage, création/destruction des nœuds dom dans la couche de vue
  • 4. Intégrez le dom virtuel, ajoutez la fonction dom diff et améliorez l'efficacité du rendu
  • 5. Fournir une fonction d'injection de dépendances pour réaliser l'inversion du contrôle
  • 6. Effectuez des contrôles de sécurité sur le contenu de l'affectation de innerHTML pour empêcher toute injection malveillante
  • 7. Mettre en œuvre la logique de collection de modèles, où chaque modèle n'a qu'une seule valeur
  • 8. Utilisez le setter dans es5 pour modifier l'implémentation de la méthode set, facilitant ainsi la modification du modèle
  • 9. Ajoutez un contrôle sur les attributs et les CSS dans la couche de vue
  • 10. Prend en charge une syntaxe similaire aux doubles accolades dans AngularJS, liant seulement une partie du HTML
  • ……

Un cadre complet doit subir d'innombrables améliorations et modifications. Ce n'est que la première étape. Le chemin est encore long. J'espère que tout le monde continuera à travailler dur.

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn