Empfohlenes Lesen:
Implementieren Sie sehr einfache JS-Zwei-Wege-Datenbindung
MVVM ist ein sehr beliebtes Entwicklungsmodell für das Web-Front-End. Die Verwendung von MVVM kann unseren Code mehr auf den Umgang mit Geschäftslogik als auf die Betreuung von DOM -Operationen konzentrieren. Derzeit umfassen die berühmten MVVM -Frameworks Vue, Avalon, React usw. Diese Frameworks haben ihre eigenen Vorteile, aber die Implementierungsidee ist ungefähr gleich: Datenbindung + Aktualisierung. Aus Neugier und der Bereitschaft zum Kampf schrieb ich auch die einfachste MVVM -Bibliothek (MVVM.Js) entlang dieser Richtung mit insgesamt mehr als 2.000 Codezeilen. Die Benennung und Verwendung von Anweisungen ähnelt Vue. Hier werde ich die Prinzipien der Implementierung und meiner Code -Organisationsideen teilen.
Ideensortierung
MVVM ist konzeptionell ein Muster, das die Ansichten von der Datenlogik wirklich trennt, und ViewModel steht im Mittelpunkt des gesamten Musters. Um ViewModel zu implementieren, müssen Sie das Datenmodell (Modell) und die Ansicht (Ansicht) in Verbindung bringen. Die gesamte Implementierungsidee kann einfach in 5 Punkte zusammengefasst werden:
Implementieren Sie einen Compiler, um Anweisungen für jeden Knoten eines Elements zu scannen und zu extrahieren.
Implementieren Sie einen Parser, um die Anweisungen für das Element zu analysieren, mit denen die Absicht der Anweisung über eine Aktualisierungsfunktion in die DOM aktualisiert werden kann (ein Modul, das möglicherweise speziell für die Ansicht erfrischend verantwortlich ist, kann in der Mitte erforderlich sein). Wenn Sie beispielsweise den Knoten <p v-show = "isShow"> </p> analysieren, erhalten Sie zunächst den Wert von Isshow im Modell und ändern Sie den Knoten.Style.Sisplay nach ISSOW, um die Anzeige und das Verstecken der Elemente zu steuern.
Durch die Implementierung eines Beobachters kann die Aktualisierungsfunktion jeder Anweisung in Parser mit den dem Modell entsprechenden Feldern verknüpft werden.
Implementieren Sie einen Beobachter, um die Wertänderung aller Felder des Objekts zu überwachen. Sobald die Änderung eintritt, kann der neueste Wert erhalten werden und der Benachrichtigungsrückruf kann ausgelöst werden.
Verwenden Sie den Beobachter, um eine Überwachung des Modells im Beobachter festzulegen. Wenn sich ein Wert im Modell ändert, wird die Überwachung ausgelöst. Nachdem der Beobachter den neuen Wert erhalten hat, ruft er die in Schritt 2 zugeordnete Aktualisierungsfunktion auf, die den Zweck der Aktualisierung der Ansicht erfüllen kann, während sich die Daten ändern.
Effektbeispiel
Schauen wir uns zunächst das endgültige Verwendungsbeispiel an, das der Instanziierung anderer MVVM -Frameworks ähnelt:
<div id="mobile-list"><h1 v-text="title"></h1><ul><li v-for="item in brands"><b v-text="item.name"></b><span v-show="showRank">Rank: {{item.rank}}</span></li></ul></div>var element = document.querySelector('#mobile-list');var vm = new MVVM(element, {'title' : 'Mobile List','showRank': true,'brands' : [{'name': 'Apple', 'rank': 1},{'name': 'Galaxy', 'rank': 2},{'name': 'OPPO', 'rank': 3}]}); vm.set ('title', 'Top 3 mobile Rangliste'); // => <h1> Top 3 mobile Rangliste </h1>Modulabteilung
Ich habe MVVM in fünf Module unterteilt, um zu implementieren: Kompilierungsmodul -Compiler, Parser, Aktualisierung des Aktualisierungsmoduls, Beobachter des Datenabonnementmoduls und Datenhörmodul -Beobachters. Der Vorgang kann kurz beschrieben werden als: Nach dem Kompilierer des Befehls werden die Anweisungsinformationen zum Parsen an den Parser übergeben. Der Parser aktualisiert den Anfangswert und zeichnet sich dem Beobachter für Änderungen der Daten ab. Der Beobachter überwacht die Daten und füttert sie dann dem Beobachter zurück. Der Beobachter benachrichtigt den Updater des Änderungsergebnisses und findet die entsprechende Aktualisierungsfunktion, um die Ansicht zu aktualisieren.
Der obige Vorgang ist in der Abbildung dargestellt:
Das Folgende ist eine Beschreibung der Grundprinzipien der Implementierung dieser fünf Module (der Code wird nur in den Schlüsselteilen veröffentlicht. Gehen Sie bitte zu meinem GitHub, um die vollständige Implementierung zu erhalten).
1. Compile Modul Compiler
Die Verantwortung des Compilers besteht hauptsächlich darin, Anweisungen für jeden Knoten des Elements zu scannen und zu extrahieren. Da der Kompilierungs- und Parsingprozess den gesamten Knotenbaum um ein Vielfaches durchquert, um die Kompilierungseffizienz im MVVM -Konstruktor zuerst zu verbessern, konvertieren Sie das Element zunächst in eine Kopie von Dokumentfragmenten. Das Kompilierungsobjekt ist dieses Dokumentfragment und sollte kein Zielelement sein. Nachdem alle Knoten kompiliert wurden, wird das Dokumentfragment zum ursprünglichen realen Knoten zurückgegeben.
vm.com plieElement implementiert das Scannen und Anweisungsextraktion aller Knoten des Elements:
vm.comPileElement = function (Fragment, root) {var node, childnodes = fragment.childnodes; // scannen Sie den untergeordneten Knoten für (var i = 0; i <childnodes.length; i ++) {node = childnodes [i]; if (this.hasdirective (node) {{thiscress {thiscress {$. untergeordneter Knoten des untergeordneten Knotens if (node.childnodes.length) {this.comPileElement (Knoten, false);}} // Scannen Sie die untergeordneten Knoten if (root) if (root) {this.comPileAllnodes ();}}Die VM.comPileallnodes -Methode erstellt jeden Knoten in diesem. Nach dem Kompilieren eines Knotens wird er aus der Cache -Warteschlange entfernt. Gleichzeitig überprüfen Sie dies. Sie können Dokumentfragmente an den realen Knoten anhängen.
2. Parser Parsing Modul Parser
Wenn der Compiler Compiler die Anweisungen aus jedem Knoten extrahiert, kann er zum Parsen an den Parser gesendet werden. Jede Anweisung hat eine andere Parsing -Methode. Alle Anweisungen müssen nur zwei Dinge tun: Eine besteht darin, den Datenwert auf die Ansicht (Anfangszustand) zu aktualisieren, und der andere soll die Aktualisierungsfunktion für die Änderungsüberwachung des Modells abonnieren. Hier verwenden wir das Parsen-V-Text als Beispiel, um die allgemeine Analysemethode einer Richtlinie zu beschreiben:
parser.parsevText = function (node, model) {// den im Modell var text definierten Anfangswert abrufen. {node.textContent = last; // updater.UpDatenodetEXTContent (Knoten, Text);});}3.. Datenabonnementmodulbeobachter
Im vorherigen Beispiel bietet Watcher eine Uhrenmethode zur Abonnement von Datenänderungen. Ein Parameter ist das Modellfeldmodell und der andere ist die Rückruffunktion. Die Rückruffunktion wird durch Beobachter ausgelöst. Der neue Wert, der letzte und alte Wert alt ist, werden im Parameter übergeben. Nachdem der Beobachter den neuen Wert erhalten hat, kann er den entsprechenden Rückruf (Aktualisierungsfunktion) des Modells finden, um die Ansicht zu aktualisieren. Modell- und Aktualisierungsfunktionen sind eine Eins-zu-Viele-Beziehung, dh ein Modell kann so viele Rückruffunktionen (Aktualisierungsfunktionen) haben, die es umgehen, z.
Datenabonnement zu Watcher hinzufügen. Die Watch -Implementierungsmethode lautet:
watcher.watch = function (field, callback, context) {var callbacks = this. $ watchcallbacks; if (! object.hasownProperty.call (this. $ model, field)) {console.warn ('Das Feld:' + field + 'existiert nicht in Modell!'); [];} // cache callbacks [field] .push ([Callback, Kontext]);}Wenn sich das Feld des Datenmodells ändert, löst der Beobachter alle Rückrufe im Cache -Array aus, die das Feld abonniert haben.
4. Datenüberwachungsmodulbeobachter
Observer ist die Kerngrundlage für die gesamte MVVM -Implementierung. Ich habe einen Artikel gelesen, in dem es darum geht, dass OO (Object.Observe) die Datenbindung der Daten bindende Revolution entzündet und dem Front-End enorme Einfluss auf das Front-End bringt. Leider hat der ES7 -Entwurf OO aufgegeben! Derzeit gibt es keine Browserunterstützung! Glücklicherweise gibt es auch Objekte. DefineProperty, die einen einfachen Beobachter simulieren kann, indem sie die Zugriffsdeskriptoren (GET and Set) von Objekteigenschaften abfangen:
// Abfangen die GET- und Set -Methoden der Prop -Eigenschaft des Objektobjekts.DefineProperty (Objekt, Prop, {get: function () {return this.getValue (Objekt, Prop);}, set: function (newValue) {var oldValue = this.getValue (Objekt, Prop); if (newValue; Auslöser ändern Callback this.triggerChange (Prop, NewValue, OldValue);}}});Dann gibt es eine andere Frage: Wie kann man Array -Operationen überwachen (Push, Verschiebung usw.)? Alle MVVM -Frameworks werden durch Umschreiben des Prototyps des Arrays implementiert:
observer.rewriteArrayMethods = function (array) {var self = this; var arrayProto = array.Prototype; var arrayMethods = Object.create (arrayProto); var methodien = 'push | pop | schieben | methods.forEach(function(method) {Object.defineProperty(arrayMethods, method, function() {var i = arguments.length;var original = arrayProto[method];var args = new Array(i);while (i--) {args[i] = arguments[i];}var result = original.apply(this, args);// Trigger callback self.triggerChange(this, Methode); Rückgabeergebnis;});}); Array .__ proto__ = arrayMethods;}Diese Implementierungsmethode wird von VUE verwiesen. Ich denke, es wird sehr gut verwendet, aber das Längenattribut des Arrays kann nicht gehört werden
5. Aktualisierung des Aktualisierungsmoduls anzeigen
Updater ist unter den fünf Modulen am einfachsten. Sie müssen nur für die Aktualisierungsfunktion verantwortlich sein, die jeder Anweisung entspricht. Nach einer Reihe von Werfen und Überlassen des Endergebnisses für Ansicht oder Ereignis-Updates ist die Aktualisierungsfunktion des V-Textes beispielsweise:
updater.updatenodetextContent = Funktion (Knoten, Text) {node.textContent = text;}Aktualisierungsfunktion von V-Bind: Stil:
updater.updatenodestyle = Funktion (Knoten, Eigenschaft, Wert) {node.style [Propperty] = Wert;}Implementierung von Zwei-Wege-Datenbindung
Die bidirektionale Datenbindung von Formelemente ist eine der größten Merkmale von MVVM:
Tatsächlich ist auch das Implementierungsprinzip dieser magischen Funktion sehr einfach. Es gibt nur zwei Dinge zu tun: Eine besteht darin, den Formularwert zu aktualisieren, wenn sich die Daten ändert, und der andere ist die Aktualisierung der Daten, wenn sich der Formularwert ändert, so dass der Datenwert an den Formularwert gebunden ist.
Datenänderungen zum Aktualisieren von Formularwerten können einfach mit dem oben genannten Beobachtermodul durchgeführt werden:
watcher.watch (Modell, Funktion (letztes, alt) {input.Value = last;}); 'Um Daten in Formularänderungen zu aktualisieren, müssen Sie nur die Ereignisse des Formulars in Echtzeit anhören und die entsprechenden Felder des Datenmodells aktualisieren:
var model = this. $ model; input.adDeVentListenr ('change', function () {modell [field] = this.value;}); 'Andere Formulare Radio, Checkbox und Select sind dasselbe Prinzip.
Das obige, der gesamte Prozess und die grundlegenden Implementierungsideen jedes Moduls wurden erklärt. Als ich zum ersten Mal einen Artikel in der Community veröffentlichte, sind meine Fähigkeiten zum Ausdruck meiner Sprachausdruck nicht sehr gut. Wenn falsche Worte und schlechte Dinge geschrieben sind, hoffe ich, dass jeder sie kritisieren und korrigieren kann!
Abschluss
Ich probiere diesen einfachen mvvm.js aus, weil ich Vue.js in meinem Framework -Projekt verwendet habe, aber ich habe gerade sein Anweisungssystem verwendet. Viele Funktionen wurden nur etwa ein Viertel verwendet. Ich dachte, es wäre ausreichend, nur Datenbindung und View-Refresh zu implementieren. Infolgedessen habe ich keine solche JavaScript -Bibliothek gefunden, also habe ich selbst ein solches Rad erstellt.
Obwohl die Funktionen und die Stabilität weitaus weniger als beliebte MVVM -Frameworks wie VUE sind und die Code -Implementierung möglicherweise relativ rau ist, wurde viel Wissen hinzugefügt, indem dieses Rad aufgebaut wird ~ Fortschritt liegt beim Werfen!
Gegenwärtig implementiert meine MVVM.js nur die grundlegendste Funktion. Ich werde es in Zukunft weiter verbessern und stärken. Wenn Sie interessiert sind, besprechen Sie es bitte zusammen und verbessern Sie es zusammen ~